树的重心
定义:找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心,删去重心后,生成的多棵树尽可能平衡。
算法流程
首先,利用前向星存边建立边表。由于无向,所以要连两次边。我们用一次 建立以1为根节点时每个结点所在子树的结点数。
接下来考虑把这个点删掉的结果,如果一个非根结点有 个儿子,那么删掉这个点之后会有 个连通分量,因为这个结点不是根节点,那么除了这个结点及它的所有子树外还有一个连通分量,我们将之称为这个结点的上方子树。接下来,计算这些子树(包括上方子树)的 再从中找个最大值,因为总共有 个结点所以有 个最大值,接下来在其中挑一个最小的即可,当这个值取到最小时的那个结点就是这棵树的重心。
的计算方法:
- 结点 的子树,直接利用 即可。
- 结点 的上方子树,可以利用求补集的思想,总共有 个结点,这个结点及其所有子树的结点和为 , 那么它的上方子树的 就是 。
上述的计算过程都在 中执行完成,故时间复杂度和空间复杂度均为
代码:
struct Tree {
struct edgetype {
int to, next;
};
int n, root, maxsize;
std::vector< edgetype > edge;
std::vector< int > head, size;
Tree() : edge(0), head(0), size(0) {}
Tree(int n) : n(n), head(n + 1, -1), size(n + 1) {}
inline void AddEdge(int from, int to) {
edge.push_back((edgetype){to, head[from]});
head[from] = edge.size() - 1;
}
void center(int u, int p) {
size[u] = 1;
int ret = 0;
for (int i = head[u]; i != -1; i = edge[i].next) {
int v = edge[i].to;
if (v == p) continue;
center(v, u);
size[u] += size[v];
ret = std::max(ret, size[v]);
}
ret = std::max(ret, n - size[u]);
if (ret < maxsize) {
maxsize = ret;
root = u;
}
}
};