题意
一棵n个点的有根树,1号点为根,相邻的两个节点之间的距离为1。树上每个节点i对应一个值k[i]。每个点都有一个颜色,初始的时候所有点都是白色的。
你需要通过一系列操作使得最终每个点变成黑色。每次操作需要选择一个节点i,i必须是白色的,然后i到根的链上(包括节点i与根)所有与节点i距离小于k[i]的点都会变黑,已经是黑的点保持为黑。问最少使用几次操作能把整棵树变黑。
分析
首先呢,涂色肯定是从叶子节点开始涂的,因为没有其他节点可以使叶子节点涂色,如图。
现在我们已经将叶子节点涂色了,要开始涂红色箭头指的点,记为 。
那么,这次我们是使 到
往上
个点都涂色吗?
其实,这样答案并不一定是最优的。
可能某个红色点可以延伸到深度更小的点,这样答案会更优。
因此,我们需要看红色的点中能涂到的最小深度为多少,也就是 。
然后,我们再用一个数组 ,表示这个点往上总共还能覆盖多少个,于是有
如果 为
,说明下面的点都无法覆盖这个点。因此需要在红色点中找能爬到的最小深度,记为
,然后更新
为
,并使答案加一。
代码如下
#include <bits/stdc++.h>
#include<ext/pb_ds/hash_policy.hpp>
#include<ext/pb_ds/assoc_container.hpp>
#define N 100005
using namespace __gnu_pbds;
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
struct custom_hash {
static uint64_t splitmix64(uint64_t x) {
x += 0x9e3779b97f4a7c15;
x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;
x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
return x ^ (x >> 31);
}
size_t operator()(uint64_t x) const {
static const uint64_t FIXED_RANDOM = chrono::steady_clock::now().time_since_epoch().count();
return splitmix64(x + FIXED_RANDOM);
}
};
LL z = 1;
int read(){
int x, f = 1;
char ch;
while(ch = getchar(), ch < '0' || ch > '9') if(ch == '-') f = -1;
x = ch - '0';
while(ch = getchar(), ch >= '0' && ch <= '9') x = x * 10 + ch - 48;
return x * f;
}
int ksm(int a, int b, int p){
int s = 1;
while(b){
if(b & 1) s = z * s * a % p;
a = z * a * a % p;
b >>= 1;
}
return s;
}
struct node{
int a, b, n;
}d[N * 2];
int h[N], k[N], dep[N], f[N], g[N], fa[N], tot, cnt;
void cr(int a, int b){
d[++cnt].a = a; d[cnt].b = b; d[cnt].n = h[a]; h[a] = cnt;
}
void dfs(int a){
int i, b;
g[a] = dep[a] - k[a] + 1;
for(i = h[a]; i; i = d[i].n){
b = d[i].b;
if(b == fa[a]) continue;
fa[b] = a;
dep[b] = dep[a] + 1;
dfs(b);
f[a] = max(f[a], f[b] - 1);
g[a] = min(g[a], g[b]);
}
if(!f[a]) f[a] = dep[a] - g[a] + 1, tot++;
}
int main(){
int i, j, n, m, a, b;
n = read();
for(i = 2; i <= n; i++){
a = read();
cr(a, i); cr(i, a);
}
for(i = 1; i <= n; i++) k[i] = read();
dfs(1);
printf("%d", tot);
return 0;
}

京公网安备 11010502036488号