这个树形dp没想出来…好菜orz
首先考虑如果覆盖了一个点x,那么对于他的一个儿子y来说,它的子树内的前D-1层可以不用覆盖了,只需覆盖剩余点即可。
因此我们考虑设计状态f[x][j],表示x子树外面的点已经帮着覆盖了前j层,覆盖子树内剩余必须点的最小花费。(x为第一层)
转移也很明显:
f[x][j]+=f[y][j−1]
f
[
x
]
[
j
]
+
=
f
[
y
]
[
j
−
1
]
但是我们发现一个问题,f[x][0]怎么计算呢?即最小代价怎么算呢,毕竟不同的子树之间还会影响…
于是我们增设状态g[x][j],表示x子树被完全覆盖,且覆盖了x外面j层的最小花费。(x为外面第0层)
可以用f和g来更新g,然后f[x][0]=g[x][0]。
然后就可以转移了
复杂度
O(nm)
O
(
n
m
)
#include <bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define ll long long
#define N 500010
inline char gc(){
static char buf[1<<16],*S,*T;
if(T==S){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
return *S++;
}
inline int read(){
int x=0,f=1;char ch=gc();
while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=gc();}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
return x*f;
}
int n,m,h[N],num=0,w[N],f[N][22],g[N][22];
//f[x][j],x子树外面的点已经帮着覆盖了前j层,覆盖子树内剩余必须点的最小花费。(x为第一层)
//g[x][j],x子树被完全覆盖,且覆盖了x外面j层的最小花费。(x为外面第0层)
bool mk[N];
struct edge{
int to,next;
}data[N<<1];
void dfs(int x,int Fa){
f[x][0]=g[x][0]=mk[x]?w[x]:0;
for(int i=1;i<=m;++i) g[x][i]=w[x];g[x][m+1]=inf;
for(int ii=h[x];ii;ii=data[ii].next){
int y=data[ii].to;if(y==Fa) continue;dfs(y,x);
for(int i=0;i<=m;++i) g[x][i]=min(g[x][i]+f[y][i],g[y][i+1]+f[x][i+1]);
for(int i=m;i>=0;--i) g[x][i]=min(g[x][i],g[x][i+1]);
f[x][0]=g[x][0];
for(int i=1;i<=m;++i) f[x][i]+=f[y][i-1];
for(int i=1;i<=m;++i) f[x][i]=min(f[x][i],f[x][i-1]);
}
}
int main(){
// freopen("a.in","r",stdin);
n=read();m=read();
for(int i=1;i<=n;++i) w[i]=read();
int owo=read();
while(owo--) mk[read()]=1;
for(int i=1;i<n;++i){
int x=read(),y=read();
data[++num].to=y;data[num].next=h[x];h[x]=num;
data[++num].to=x;data[num].next=h[y];h[y]=num;
}dfs(1,0);
printf("%d\n",f[1][0]);
return 0;
}