黑白树
题目描述:
一棵n个点的有根树,1号点为根,相邻的两个节点之间的距离为1。树上每个节点i对应一个值k[i]。每个点都有一个颜色,初始的时候所有点都是白色的。你需要通过一系列操作使得最终每个点变成黑色。每次操作需要选择一个节点i,i必须是白色的,然后i到根的链上(包括节点i与根)所有与节点i距离小于k[i]的点都会变黑,已经是黑的点保持为黑。问最少使用几次操作能把整棵树变黑。
Solution: 根据题目描述,我们很容易想到从叶子节点 x 以 k[x] 的距离往上覆盖,当k[x] 为0时,染色数就++,但这时候选择哪个节点再进行染色是最优的呢?是当前恰好不能被 x 覆盖的当前节点,还是已经被 x 覆盖的结点中 k[i] (动态维护) 最大的一个和当前结点的 k 相比中较大的一个?所以当然是后者,但是你会有疑问,那已经被覆盖了的点不是不能再被染色了吗?注意这时候只是选择要染的点,而不是按照这个顺序染,当要染的点都挑选完毕,你可以从根往下染嘛。是吧~
#include<iostream>
#include<cstring>
using namespace std;
const int N = 1e5+7;
int h[N],e[N*2],ne[N*2],idx;
int k[N],ans;
void add(int a,int b){
e[idx] = b,ne[idx] = h[a],h[a] = idx ++;
}
int DFS(int u,int fa)
{
int num = 0;
for(int i=h[u];i!=-1;i=ne[i]){
int j = e[i];
if(j==fa) continue;
num = max(num,DFS(j,u)); //所有已经染过色的孩子能覆盖到的最大值
}
if(num==0){ //所有染过色的孩子都覆盖不到它
ans++;return k[u]-1;
}
k[fa] = max(k[fa],k[u]-1); //维护每个结点能往上覆盖的最大距离
return num-1;
}
int main()
{
int n,t;
cin>>n;
memset(h,-1,sizeof h);
for(int i=2;i<=n;i++){
cin>>t;
add(t,i),add(i,t);
}
for(int i=1;i<=n;i++) cin>>k[i];
DFS(1,0);
cout<<ans<<endl;
return 0;
}