如果树 \(T\) 删去某个节点 \(u\) 后,所有子树大小均不超过 \(n / 2\),则 \(u\) 为 \(T\) 的重心。
性质
- 一棵树的重心一定在直径上。
- 重心只有 1/2 个。如果有两个则相邻,删去它们的连边后变成两个 \(n / 2\) 的子树。
- 树中所有结点到某个结点的距离和中,到结点 \(u\) 的距离和最小。
- 树的重心之和结构以及点权相关,与边权无关。
更多的见题目。
CSP-S2019 树的重心
树的重心其实是任选一个节点为根, dfs 序中最后一个 \(sz \le n / 2\) 的节点。(深度最大。)如果有两个还有一个一定是它的父亲。所以用线段树维护子树大小,进行线段树二分即可。
于是这个题就有了一个及其暴力的做法:对于 \((u, v)\),子树部分的很好求;删去 \(u\) 子树部分的,相当于 \(u\) 到根节点路径的 \(sz\) 都减掉,查询时忽略 \(u\) 的子树,用个树链剖分 + 线段树不难做到 \(O(n \log ^ 2 n)\)。应该过不了。
另一种做法是这样的,求出每个点的重儿子 \(son_u\),重心一定是在根所在的重链上的。(显然跟轻儿子没啥关系。)因为重链上的子树大小递减,直接维护倍增数组,查询时跳即可。
而这个倍增数组是可以换根维护的,根从 \(x\) 移到其儿子 \(u\) 时,只有 \(son_x\) 与 \(son_u\) 可能发生变化。
- 若 \(u = son_x\),\(son_x\) 变为 \(x\) 其他儿子中最重的,\(son_u\) 变为 \(son_u,x\) 中重的那个。
- 若 \(u \ne son_x\),\(son_u\) 一定是变为 \(x\),\(son_x\) 不变。
根据其他的倍增数组可以快速修改。时间复杂度:\(O(n \log n)\)。
ZJOI2015 幻想乡战略游戏
给定一棵带点权和边权的数,需要支持动态修改点权 \(d_u\) 以及查询 \(\min\limits_u \{ \sum (d_v \times dis(u,v)) \}\)
\(u\) 其实就是重心。 如果只需要求重心的话,用上个题的做法即可,但还需要求距离和就比较麻烦。
转化一下,就是要找到一个节点 \(u\),删去它之后所有子树点权和 \(\le 总点权和 / 2\)。
介绍一种可以同时求出 \(u\) 和答案的做法:点分树(边分树)。因为点分治本来就是依靠重心子树大小 \(\le n / 2\) 的重心时间复杂度才正确的,所以它来解决这个东西是很合理的。
这个题保证了度数 \(\le 20\),但做个三度化其实一样的。因为边分树只有两个子树,就讨论这种比较简单的情况。
首先要把整棵子树外的点“挂到” 叶子节点上,这样才能保证是对的。
设边分重心为 \((u, v)\),两个子树为 \(u\) 子树、\(v\) 子树。找到带权重心是不难的,主要就是求距离和。设 \(s(x)\) 表示 \(x\) 的子树内的点到 \(x\) 的距离和,\(c(x)\) 表示子树大小。可以得到重心 \(x\) 在那个子树,不妨设为 \(subtree(u)\),\(v\) 子树的贡献是 \(s(v)\) 。递归到下一层继续计算。
动态维护 \(s(x)\),修改时枚举 \(x\) 所在的边分中心,根据 \(dep\) 修改即可。
再提一句如何实现把外部的点挂到叶子节点上。求出外部点到叶子节点 $leaf $的距离和,暴力加到叶子节点对应的所有 \(v\) 的 \(s(v)\) 即可。(要算上 \(v\) 到 \(leaf\) 的距离。)
时间复杂度:\(O(q \log^2 n)\)。