树的直径、重心、中心 学习笔记
引入——什么是树?
树是一个简单无环无向连通图,其由 \(n\) 个点与 \(n-1\) 条边组成。它是一种特殊的图。
树的直径
定义
定义树上任意两点之间最长的简单路径为树的直径。
一棵树可能拥有多条直径。
求解
两次 DFS 求法
在没有负边权的情况下,我们一般使用两次 DFS 求树的直径:
第一次 DFS 从任意位置(比如 \(1\))出发,找到距离最远的点 \(x\),\(x\) 就是某一条直径的端点之一;
第二次 DFS 从点 \(x\) 出发,找到距离最远的点 \(y\),\(x\) 到 \(y\) 的路径即为一条直径。
树形 DP 求法
有负边权的情况下,就不能使用两次 DFS 求法咯。这时可以用树形 DP 来求解。
任取一个节点(通常取 \(1\) 号节点)作为根节点,对于每个节点 \(x\) ,考虑以 \(x\) 为根节点的子树中经过 \(x\) 的最长的路径,这个路径长度等于 \(x\) 向下的最长路径长度加上 \(x\) 向下的次长路径长度,开两个 DP 数组,DFS 时分别记录这些信息即可。
树的重心
定义
选择一个合适的根节点,使得根节点的子树能够尽可能的“均匀”,这个根节点就是树的重心。
“均匀”具体是指什么呢?我们这里把最大子树节点数认为是“均匀”的程度。
最大子树节点数越小,就越均匀。使得最大子树节点数最小的根节点,就是重心。
重心最多只有两个,若有两个一定相邻。
性质
- 以重心作为根节点,根节点的最大子树节点数不会超过 \(\lceil \frac{n}{2} \rceil\)。
- 树上所有点到某个点的距离之和中,到重心的最小。
- 把两棵树用一条边连起来,形成的新的树的重心在原来两树重心之间的路径上。
- 在一颗树上添加一个叶子节点,重心最多向叶子节点方向移动一条边。
求解
暴力做法
枚举每个节点作为根节点,跑一通,然后看哪个最优即可。
换根 DP 做法
根据性质:
树上所有点到某个点的距离之和中,到重心的最小。
考虑用这个和来判断重心。
先跑出每个子树的大小,然后 BFS 求出以 \(1\) 为根时的答案,从 \(1\) 开始跑,不断换根去做 DP 最后找最小值即可。换根式子 \(dp_{u} = dp_{fa} - sz_{u} + (n-sz_{u})\),是分类讨论 \(u\) 子树内和 \(u\) 子树外的。
树的中心
定义
如果树上一点到树上任意一点的最远距离在所有点中最短,那么该点就是树的中心。
树的中心不一定唯一,最多有 \(2\) 个,并且如果有 \(2\) 个,则它们一定相邻。
性质
- 树的中心一定在树的直径上。
- 树的所有点到其最远点的路径一定相交于树的中心上。
- 在两颗树中间连一条边以合并成一颗树时,连接两树的中心可以让新树的直径最小。
- 无权树的中心到任意一点的距离不超过直径长度的一半。
求解
树形 DP 求法
记录从每个点出发的在其子树中的最长链、次长链(类似树形 DP 求解方法),以及连向父节点的外部最长链。对内部长链和外部长链长度求 \(\max\),所有节点的情况进行比对即可找出中心。
换根 DP 求法
根节点的最长路一定向下,所以我们也可以使用换根 DP。每次搜索邻居时,把邻居作为根节点,当前节点作为邻居的儿子,新的根节点的最长路、次长路也同样可以快速确定。一样可以进行求出中心。
例题选讲
P14076 货物运输
怎么是橙题。
伪装版本树的直径。
首先发现一个性质,那就是当你要回到根节点的情况下是每条边都会走两遍的,一来一回。不难证明这是最优的。而这里不要求你回到根节点,为了让这个道路总和尽可能短,我们就尽可能找一条最长的链的长度给它减去。是树的直径吗?其实不是喔,因为这边固定了得从根出发,因此从根出发 DFS 一下就完事儿了。
P1364 医院设置
也是树的重心,但是每个节点有了点权,因此做的时候会有些差别。总体来说还是很简单哒,粘个板子改改就过了。
CF120F Spiders
首先求出每棵树的直径。那么想让最终的直径最大化肯定就是每个的直径加起来啦,容易发现这是很好实现的。
CF1406C Link Cut Centroids
首先肯定是找重心嘛。如果重心唯一,那就随便删条边再加回来就行了。
不唯一的话,设这两个重心分别为 \(A\) 和 \(B\)。让 \(B\) 为根,从 \(A\) 往下找,拎一个叶子结点出来,塞到 \(B\) 这一边的子树里(这里可以理解为让 \(A\) 为根的情况),于是就可以成功让 \(B\) 是唯一的重心了。
CF685B Kay and Snowflake
询问次数和节点个数的个数级别是差不多的,显然可以一遍跑出所有点为根的子树的答案。换根 DP 嘛。
考虑节点 \(x\) 和其每个儿子节点 \(y\) ,若 \(x\) 子树的重心在 \(y\) 子树内,那么显然的,\(x\) 子树的重心在 \(y\) 子树上方。
这样的话,我们枚举每个 \(x\) 的儿子 \(y\),\(z\) 为当前考虑的节点,初始为 \(y\) 子树的重心。
我们每次让 \(z\) 往上跳然后不断更新最优解。由于 \(z\) 只会跳到每个点 \(1\) 次,所以时间复杂度是线性的。
CF455C Civilization
发现连接两棵树,如果要求拼接后的树的直径尽可能短,考虑把两棵树的直径的中间点相连;令两棵树的直径长度原本分别是 \(X\) 和 \(Y\),那么拼接后的就是 \(\lceil \frac{X}{2} \rceil + \lceil \frac{Y}{2} \rceil + 1\),当然要和 \(X\) 和 \(Y\) 取 \(\max\)。之后用并查集维护连通性情况附带这个直径信息直接做就可以了。
CF911F Tree Destruction
找出直径,然后沿着直径把图拉成一条链,链旁边会长很多小枝小叶。真的就像一根树枝的样子诶。
然后枚举小枝小叶上的每个点让它和直径两端点之一(选距离更远的那一端)算贡献,然后就把它们删掉,按顺序来别破坏连通性了。
最后只剩一根光秃秃的直径,左右依次慢慢儿删就完了。
总结
树的直径、重心和中心的定义都很简单,求法也不难,但是有很多特殊的性质,出题考你时就喜欢考这些性质,异或是它们的某些深入剖析才能发现的本质。说来说去,板子重要也不重要,更重要的是本质上的内容。只要能完全理解这三样东西的本质,其实就非常简单了。
Thanks reading.

浙公网安备 33010602011771号