题目大意: 给出一棵树,有 m m m 个关键点,在一个点放置侦查守卫可以监视距离为 d d d 以内的点,在每个点放侦查守卫有不同代价,求最小代价监视所有关键点。
题解
不难想到树形 d p dp dp,但这题状态设计十分巧妙,设 f x , i f_{x,i} fx,i 表示 x x x 子树内关键点都被监视了,且还能监视到 x x x 距离小于等于 i i i 的点最小代价, g x , i g_{x,i} gx,i 表示 x x x 子树内到 x x x 距离小于 i i i 的都没被监视,但大于等于 i i i 的都被监视了的最小代价。
定义上需要注意的是, f x , 0 f_{x,0} fx,0 只能监视子树,不能再往外监视,而 g x , 0 g_{x,0} gx,0 表示子树内都被监视,因为不存在到 x x x 距离小于 0 0 0 的点,也就是要注意小于等于和小于的问题。
考虑 x x x 的一棵子树 y y y 对 x x x 的贡献,先枚举出 i i i,考虑 f x , i f_{x,i} fx,i,假如 x x x 要能往外监视 i i i 层,那么必定有一个子树能往外监视 i + 1 i+1 i+1 层,这个子树可以是之前的子树,那么贡献就是 f x , i + g y , i f_{x,i}+g_{y,i} fx,i+gy,i,因为 y y y 往下 i − 1 i-1 i−1 层也可以顺便被 x x x 监视;这个子树也可以是 y y y,贡献就是 f y , i + 1 + g x , i + 1 f_{y,i+1}+g_{x,i+1} fy,i+1+gx,i+1。
再考虑 g g g 的转移,显然有 g x , 0 = f x , 0 g_{x,0}=f_{x,0} gx,0=fx,0,而对于 g x , i g_{x,i} gx,i,加上子树的 g y , i − 1 g_{y,i-1} gy,i−1 即可。
但是有一个小问题,有可能 f x , i + 1 < f x , i f_{x,i+1}<f_{x,i} fx,i+1<fx,i,即监视更多层的代价会更小,由于 f f f 的定义是恰好监视 i i i 层所以可能出现这样的情况。
不妨稍改一下定义:令 f x , i f_{x,i} fx,i 表示 x x x 往外至少能监视 i i i 层, g x , i g_{x,i} gx,i 表示 x x x 往下至多有 i − 1 i-1 i−1 层未被监视,那么对 g , f g,f g,f 再分别取一个前后缀 min \min min 即可。
代码如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 500010
int n,d,m,a[maxn];
struct edge{int y,next;}e[maxn<<1];
int first[maxn],len=0;
void buildroad(int x,int y){e[++len]=(edge){y,first[x]};first[x]=len;}
bool key[maxn];
int f[maxn][20],g[maxn][20];
void dfs(int x,int fa){
for(int i=1;i<=d;i++)f[x][i]=a[x];
if(key[x])f[x][0]=g[x][0]=a[x];
f[x][d+1]=1e9;
for(int i=first[x];i;i=e[i].next){
int y=e[i].y;if(y==fa)continue;
dfs(y,x);
for(int j=0;j<=d;j++)f[x][j]=min(f[x][j]+g[y][j],f[y][j+1]+g[x][j+1]);
for(int j=d-1;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[y][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",&a[i]);
scanf("%d",&m);
for(int i=1,x;i<=m;i++)scanf("%d",&x),key[x]=true;
for(int i=1,x,y;i<n;i++){
scanf("%d %d",&x,&y);
buildroad(x,y);buildroad(y,x);
}
dfs(1,0);
printf("%d",g[1][0]);
}