📅  最后修改于: 2023-12-03 15:27:08.603000             🧑  作者: Mango
在一棵带权树上,定义树根为根节点的权重和最小的节点。现在需要对这棵树进行一次操作,即选择一条边将其进行反转(即原本是从父节点到子节点的边变成从子节点到父节点的边)。操作后,根节点会变成其他节点,且需要重新求得新的根节点。本文将介绍如何选择一条最小的边进行反转,使得生成的新根节点的权重和最小。
该问题可以通过两次 DFS 来解决。
首先任选一个节点为根节点,在每个节点处统计子树大小 $size$ 和子树内结点权重和 $sum$。我们可以用以下代码实现:
int dfs1(int u, int fa) {
size[u] = 1;
for (int v : adj[u]) {
if (v != fa) {
size[u] += dfs1(v, u);
sum[u] += sum[v] + size[v];
}
}
return size[u];
}
接下来,我们从根节点出发,遍历树中的所有节点,计算以当前节点作为根节点时,树内节点的权重和。我们可以定义一个变量 $cur$ 来保存当前根节点的权重和。每经过一个节点 $u$,就将 $cur$ 减去子树内节点权重和 $sum[u]$,再加上剩余节点个数(不包括 $u$ 子树内节点数)内节点权重和 $sum_{rest}$。再根据剩余节点数量,可以利用 $size$ 计算出 $sum_{rest}$。最后,就可以更新最小的根节点和最小的反转边了。
void dfs2(int u, int fa) {
for (int v : adj[u]) {
if (v == fa) continue;
int cur = sum[u] - (sum[v] + size[v]) + (n - size[v]) * (sum[v] + size[v]);
if (cur < minW) {
minW = cur;
root = v;
flip = {u, v};
}
dfs2(v, u);
}
}
其中,$n$ 表示树的总节点数。在遍历完成后,$root$ 存储了新的根节点编号,$flip$ 存储了需要反转的边的两个端点。
本文介绍了生成根的最小边反转问题的解法,可以利用两次 DFS 来解决,在实际使用中具有较高的效率和正确性。