BZOJ 4557: [JLoi2016]侦察守卫
题目大意:
每个点有一个放置守卫的代价,同时每个点放置守卫能覆盖到的距离都为d,问覆盖所有给定点的代价是多少。
题解:
树形DP
f[x][y]表示x子树中所有点都已经覆盖完,并且x还能向上覆盖y层的最小代价。
g[x][y]表示x的y层及以下的所有点都已经覆盖完,还需要覆盖上面的y层的最小代价。
代码:
#include<cstdio> #include<algorithm> using namespace std; int n,d,cnt,m,last[1000005],f[1000005][21],g[1000005][21],vis[1000005],w[1000005]; struct node{ int to,next; }e[1000005]; void add(int a,int b){ e[++cnt].to=b; e[cnt].next=last[a]; last[a]=cnt; } void dfs(int x,int fa){ if (vis[x]) f[x][0]=g[x][0]=w[x]; for (int i=1; i<=d; i++) f[x][i]=w[x]; f[x][d+1]=1e9; for (int i=last[x]; i; i=e[i].next){ int V=e[i].to; if (V==fa) continue; dfs(V,x); for (int j=d; j>=0; j--) f[x][j]=min(f[x][j]+g[V][j],g[x][j+1]+f[V][j+1]); for (int j=d; j>=0; j--) f[x][j]=min(f[x][j],f[x][j+1]); g[x][0]=f[x][0]; for (int j=1; j<=d; j++) g[x][j]+=g[V][j-1]; for (int j=1; j<=d; j++) g[x][j]=min(g[x][j],g[x][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; i<=m; i++){ int x; scanf("%d",&x); vis[x]=1; } for (int i=1; i<n; i++){ int x,y; scanf("%d%d",&x,&y); add(x,y); add(y,x); } dfs(1,0); printf("%d\n",f[1][0]); return 0; }