[JLOI2016]侦察守卫
[JLOI2016]侦察守卫
https://ac.nowcoder.com/acm/problem/20562
分析
比较奥秘重重的 状态设计。定义
为覆盖以
为根的全部子树,而且向上覆盖了
层节点。
表示,覆盖了以
为根,子树节点深度
的全部节点。
初始化
- 对于任何情况
。选择这个节点的转移。
- 如果当前点是关键点,那么
。由于必须要覆盖这个节点,那么初始化为必选状态。
- 如果当前点不是关键点,那么
。对于非关键点可以不选。
- 对于任何情况
转移
定义,不解释。
,当儿子转移到父亲,子树深度增加一。
,这个其实是滚动数组,只要我们正序枚举就可以了。
由于我们的转移只转移了端点,所以我们还要做一次前缀(后缀)最小值。
代码
#include<bits/stdc++.h>
using namespace std;
int read() {
int x = 0,f = 0;char ch = getchar();
while(!isdigit(ch)) {if(ch=='-')f=1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
return f?-x:x;
}
const int N = 550000,inf = 1e9;
int f[N][22],g[N][22],vis[N],val[N],n,m,d;
struct Edge {int to,nxt;}e[N << 1];
int head[N],ecnt;
void add(int x,int y) {e[++ecnt]=(Edge){y,head[x]};head[x]=ecnt;}
void dfs(int x,int fa) {
for(int i = 1;i <= d;i++) f[x][i] = val[x];
if(vis[x]) f[x][0] = val[x];f[x][d + 1] = inf;
g[x][0] = f[x][0];
for(int i = head[x];i;i = e[i].nxt) {
int y = e[i].to;
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;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() {
n = read();d = read();
for(int i = 1;i <= n;i++) val[i] = read();
m = read();
for(int i = 1,a;i <= m;i++) a = read(),vis[a] = 1;
for(int i = 1;i < n;i++) {
int x = read(),y = read();
add(x,y);add(y,x);
}
dfs(1,0);
printf("%lld\n",f[1][0]);
return 0;
}