Dev.c++树的基础:树是一种特殊的图,有一些特殊的性质,重心就是其一。可以用它的定义求解。
声明:感谢oi-wiki所提供的代码。在下***进行了一点点修改。这里先贴出oi-wiki提供的源代码。
声明:感谢大佬zhnzh提供的帮助。一些用词和技术方面是它所提供的。若有出错,请在评论区提出
再次声明:如果有错,不要怪我哟!不要脸的
// 这份代码默认节点编号从 1 开始,即 i ∈ [1,n]
int size[MAXN], // 这个节点的“大小”(所有子树上节点数 + 该节点)
weight[MAXN], // 这个节点的“重量”
centroid[2]; // 用于记录树的重心(存的是节点编号)
void GetCentroid(int cur, int fa) { // cur 表示当前节点 (current)
size[cur] = 1;
weight[cur] = 0;
for (int i = head[cur]; i != -1; i = e[i].nxt) {
if (e[i].to != fa) { // e[i].to 表示这条有向边所通向的节点。
GetCentroid(e[i].to, cur);
size[cur] += size[e[i].to];
weight[cur] = max(weight[cur], size[e[i].to]);
}
}
weight[cur] = max(weight[cur], n - size[cur]);
if (weight[cur] <= n / 2) { // 依照树的重心的定义统计
centroid[centroid[0] != 0] = cur;
}
}
同时贴出我讲解所需的代码
int size[MAXN], // 这个节点的“大小”(所有子树上节点数 + 该节点)
weight[MAXN], // 记录最大子树
centroid[2]; // 用于记录树的重心(存的是节点编号)
void GetCentroid(int u, int fa) { // u 表示当前节点 (current)
size[u] = 1;
weight[u] = 0;
for (int i = head[u]; i != -1; i = e[i].nxt) {
if (e[i].to != fa) { // e[i].to 表示这条有向边所通向的节点。
GetCentroid(e[i].to, u);
size[u] += size[e[i].to];
weight[u] = max(weight[u], size[e[i].to]);
}
}
weight[u] = max(weight[u], n - size[u]);
if (weight[u] <= n / 2) { // 依照树的重心的定义统计
centroid[centroid[0] != 0] = u;//这是一个小小的优化
}
}
因为对于树上的每一个点,计算其所有子树中最大的子树节点数,这个值最小的点就是这棵树的重心;
所以这里利用这个计算重心。
这里先将整棵树遍历一遍,遍历过程中,每个节点利用递归将它的子树递归出来(至于怎么弄得等会解释)。
然后算出这棵树上最大的子树,不断的更新它的重心就可以了。
这里要重点解释一下递归求解子树大小。
可以看到他在循环结束后用size记录下节点的大小,当递归到边界时,size为1.
返回上去时,size会加上边界的size。再返回,又会让这个节点的size加上他的孩子的size。
以此类推,他会把整颗子树的size都遍历出来,然后加到这个节点的size中。这就是递归求解节点大小的原理。
到现在为止,树的基础部分都讲完了。