经典的$cov-unc$树形dp(这词是你自己造的吧=。=)
设$cov[i][j]$表示覆盖完$i$的子树后至少向外再覆盖$j$层的最小代价,$unc[i][j]$表示$i$的子树中还剩下至少$j$层没有覆盖时的最小代价,然后是两个数组的抵消转移什么的
1.边界:对于每个需要覆盖的节点$i$,$unc[i][0]=cov[i][0]=cost_i$,对于不需要覆盖的节点$unc[i][0]=cov[i][0]=0$
而对于每个点$i$,又都有$unc[i][j]=0,cov[i][j]=cost_i(j∈N^*&&j<=d)$
2.$cov$的转移
对于每个距离$j$,显然有$cov[i][j]+=unc[goal[i]][j]$(覆盖它当前这个子树)
然后对于距离小于$d$的情况有$cov[i][j]=min(cov[i][j],unc[i][j+1]+cov[goal[i]][j+1])$(子树向外覆盖)
还有我们定义的是“至少”:$cov[i][j]=min(cov[i][j],cov[i][j+1])$
3.$unc$的转移
显然的,有$unc[i][0]=cov[i][0]$
子树的第$i-1$层被覆盖则自己的第$i$层被覆盖,同时也是注意定义里的“最少”:$unc[i][j]=min(unc[i][j]+unc[goal[i]][j-1],unc[i][j-1]$
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int N=500005,K=21; 6 int p[N],noww[2*N],goal[2*N]; 7 int cst[N],imp[N],unc[N][K],cov[N][K]; 8 int n,d,m,t1,t2,cnt,ans=0x3f3f3f3f; 9 void link(int f,int t) 10 { 11 noww[++cnt]=p[f]; 12 goal[cnt]=t,p[f]=cnt; 13 } 14 void DFS(int nde,int fth) 15 { 16 if(imp[nde]) unc[nde][0]=cov[nde][0]=cst[nde]; 17 for(int i=1;i<=d;i++) cov[nde][i]=cst[nde]; 18 for(int i=p[nde];i;i=noww[i]) 19 if(goal[i]!=fth) 20 { 21 DFS(goal[i],nde); 22 for(int j=d;~j;j--) 23 { 24 cov[nde][j]+=unc[goal[i]][j]; 25 if(j<d) 26 { 27 cov[nde][j]=min(cov[nde][j],cov[nde][j+1]); 28 cov[nde][j]=min(cov[nde][j],cov[goal[i]][j+1]+unc[nde][j+1]); 29 } 30 } 31 unc[nde][0]=cov[nde][0]; 32 for(int j=1;j<=d;j++) 33 unc[nde][j]=min(unc[nde][j]+unc[goal[i]][j-1],unc[nde][j-1]); 34 } 35 } 36 int main () 37 { 38 scanf("%d%d",&n,&d); 39 for(int i=1;i<=n;i++) 40 scanf("%d",&cst[i]); 41 scanf("%d",&m); 42 for(int i=1;i<=m;i++) 43 scanf("%d",&t1),imp[t1]=true; 44 for(int i=1;i<n;i++) 45 { 46 scanf("%d%d",&t1,&t2); 47 link(t1,t2),link(t2,t1); 48 } 49 DFS(1,0); 50 for(int i=0;i<=d;i++) 51 ans=min(ans,cov[1][i]); 52 printf("%d",ans); 53 return 0; 54 }