版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/qq_35950004/article/details/88627942
题目
这个题
的状态和大概的方法很好想,但是在付诸实践时却有点。。。
设
表示处理完
为根的子树后,
的子树内没被监视的点中深度最大的离
的距离为
表示处理完
为根的子树后,还可以从
往上监视
的距离。
可以得到:
然后,。。。怎么写啊。。。。。
我们不要再把树形DP的状态简单理解为一个点和一些附加信息了。
树形DP的DP顺序是按照儿子一个一个加入的。
如果不是,像上面的式子会像无形之刃,爆零预定。
我们考虑加入儿子时,设父亲为
,加入儿子
前的为
和
,因为~~可以预见 ~~转移可以原地转移,可以把
这一维省掉。
最后变成:
其实上面的没有考虑很多情况。
深度最大的不一定是
,可以比
低。
来一发前缀最小值。
把
的意义改为距离
,
的意义改为距离
那么。
上面的式子还有情况没有考虑到。
如果子树内没有需要控制的点呢?
我们需要一个
将
整体右移一位,用
存
就行。
特别注意将儿子考虑在树形DP的状态中,不然DDP怎么打啊。
AC Code:
// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define maxn 500002
#define o 22
#define inf 0x3f3f3f3f
using namespace std;
int n,d,m;
int f[maxn][25],g[maxn][25],w[maxn],tag[maxn];
int info[maxn],Prev[maxn<<1],to[maxn<<1],cnt_e=0;
void Node(int u,int v){Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v;}
void dfs(int now,int ff){
if(tag[now]) f[now][0] = g[now][0] = w[now];
for(int i=1;i<=d;i++) g[now][i] = w[now];
g[now][d+1] = inf;
for(int i=info[now];i;i=Prev[i])
if(to[i]!=ff)
dfs(to[i],now);
for(int i=info[now];i;i=Prev[i])
if(to[i]!=ff){
for(int j=d;j>=0;j--) g[now][j] = min(g[now][j]+f[to[i]][j],f[now][j+1]+g[to[i]][j+1]);
for(int j=d;j>=0;j--) g[now][j] = min(g[now][j] , g[now][j+1]);
f[now][0] = g[now][0];
for(int j=1;j<=d+1;j++) f[now][j] +=f[to[i]][j-1];
for(int j=1;j<=d+1;j++) f[now][j] = min(f[now][j] , f[now][j-1]);
}
}
int main(){
scanf("%d%d",&n,&d);
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
scanf("%d",&m);
for(int i=1,x;i<=m;i++) scanf("%d",&x),tag[x]=1;
for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
Node(u,v),Node(v,u);
}
dfs(1,0);
printf("%d\n",f[1][0]);
}