NC13249 黑白树
链接:https://ac.nowcoder.com/acm/problem/13249
题解:https://ac.nowcoder.com/discuss/401669
题目描述
一棵
n
n
n 个点的有根树,1号点为根,相邻的两个节点之间的距离为1。树上每个节点
i
i
i 对应一个值
k
[
i
]
k[i]
k[i] 。每个点都有一个颜色,初始的时候所有点都是白色的。
你需要通过一系列操作使得最终每个点变成黑色。每次操作需要选择一个节点
i
i
i,
i
i
i 必须是白色的,然后
i
i
i 到根的链上(包括节点
i
i
i 与根)所有与节点
i
i
i 距离小于
k
[
i
]
k[i]
k[i] 的点都会变黑,已经是黑的点保持为黑。问最少使用几次操作能把整棵树变黑。
输入描述:
第一行一个整数
n
(
1
≤
n
≤
1
0
5
)
n (1 ≤ n ≤ 10^5)
n(1≤n≤105)
接下来
n
−
1
n-1
n−1 行,每行一个整数,依次为
2
2
2 号点到
n
n
n 号点父亲的编号。
最后一行
n
n
n 个整数为
k
[
i
]
k[i]
k[i] (
1
≤
k
[
i
]
≤
1
0
5
1 ≤ k[i] ≤ 10^5
1≤k[i]≤105)
样例解释:
对节点3操作,导致节点2与节点3变黑
对节点4操作,导致节点4变黑
对节点1操作,导致节点1变黑
输出描述:
一个数表示最少操作次数
输入
4
1
2
1
1 2 2 1
输出
3
思路:
贪心。由题意可知,叶子节点必定要染色。
那么我们贪心的从子节点往上染色,我们看当前节点
x
x
x 的已染色的子节点有没有能将它覆盖的,如果有那说明这个点已经被染成黑色的了;如果没有那么就要选择当前节点
x
x
x 进行染色,然后借助这个点更新往上能覆盖的最大范围。
维护当前节点向上能覆盖的最大距离,可以直接维护在数组
k
k
k 中。
使用
d
f
s
dfs
dfs 从子节点向父节点更新,用数组
f
f
f 维护当前节点
x
x
x 的已染色的子节点(
f
i
f_i
fi大于0)中能向上覆盖的最大距离,
f
x
=
m
a
x
(
f
i
−
1
)
f_x=max(f_i-1)
fx=max(fi−1),
i
i
i 是
x
x
x 的所有子节点,不能覆盖
x
x
x 就对其染色,然后用
k
x
k_x
kx 更新
f
x
f_x
fx 。
注意理解这里的
k
i
k_i
ki 和
f
i
f_i
fi 的意义。
时间复杂度:
O
(
n
)
O(n)
O(n) 。
Code:
#include <iostream>
#include <vector>
using namespace std;
const int N = 1e5 + 5;
int n, k[N];
vector<int> e[N]; //存图方式,单向的 父亲 -> 孩子
int f[N], ans, x;
void dfs(int u, int fa) {
for (int i = 0; i < e[u].size(); i++) {
int v = e[u][i];
dfs(v, u);
f[u] = max(f[u], f[v] - 1);
k[u] = max(k[u], k[v] - 1);
}
if (f[u] == 0) {
f[u] = k[u];
ans++;
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n;
for (int i = 2; i <= n; i++) {
cin >> x;
e[x].push_back(i);
}
for (int i = 1; i <= n; i++) cin >> k[i];
dfs(1, 0);
cout << ans << endl;
return 0;
}