📅  最后修改于: 2023-12-03 14:49:36.773000             🧑  作者: Mango
在树的数据结构中,我们常常需要对树上的所有节点进行某种运算,使得所有节点的值变为零。本文介绍了一种常见的算法来实现这个目标。
我们定义一个节点的贡献为它本身的值加上所有子树内节点的值之和。如果我们把所有节点的贡献求出来,那么要让整棵树所有节点的值为零,我们只需要把每个节点的值变成该节点的贡献的相反数即可。
具体实现上,我们可以从叶节点开始,逐层向上递推。假设当前递推到了节点 $u$,则该节点的贡献为:
$$ c_u = v_u + \sum_{v\in son(u)} c_v $$
其中 $v_u$ 表示节点 $u$ 的初始值,$son(u)$ 表示节点 $u$ 的所有儿子节点。对于根节点,其初始值已经为 $0$,而对于叶节点,则其贡献为其初始值。
我们递推时,从叶节点开始,自下而上计算每个节点的贡献,最终得到根节点的贡献 $c_{root}$,即所有节点的初始值之和。之后,我们再自上而下,将每个节点的值变成该节点的贡献的相反数,即:
$$ v_u = -c_u\qquad(u\in tree) $$
这样,所有节点的值便都变成了零。
下面给出 C++ 的实现代码,其中 sum[u]
表示节点 $u$ 的贡献,val[u]
表示节点 $u$ 的初始值。
const int MAXN = 1e5 + 5;
int val[MAXN], sum[MAXN];
vector<int> G[MAXN];
void dfs(int u, int fa) {
if (G[u].size() == 1 && u != 1) {
sum[u] = val[u];
return;
}
for (int v : G[u]) {
if (v == fa) continue;
dfs(v, u);
sum[u] += sum[v];
}
sum[u] += val[u];
}
void solve() {
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
G[i].clear();
sum[i] = 0;
}
for (int i = 1; i < n; i++) {
int u, v;
cin >> u >> v;
G[u].push_back(v);
G[v].push_back(u);
}
for (int i = 1; i <= n; i++) {
cin >> val[i];
}
dfs(1, 0);
for (int i = 1; i <= n; i++) {
val[i] = -sum[i];
}
}
需要注意的是,我们要从叶节点开始递推,因此 dfs
函数中需要特判叶节点。如果一个节点没有任何儿子,那么它就是叶节点,且其贡献为其初始值;而如果它有儿子,那么它的贡献应该在递归儿子节点时顺带计算。