「45」树的直径
定义
给定一棵树,树中每条边都有一个权值,树中两点之间的距离定义为连接两点的路径上的边权之和。树中最远的两个节点之间的距离被称为 树的直径。
下面我们讨论的前提均为边权非负。
性质1
从任意点出发,最远点一定可以作为某条直径的端点。
任取一个点 ,找到距离 最远的点 。那么 一定是某条直径的端点。再从 出发,找到最远点 ,则 就是一条直径。
证明
将 作为根节点,任取树的一条直径 , 和 的 LCA 为点 。考虑 和 的LCA 点 与 的位置关系。
情况1
点 位于 和 之间,包括 和 。
因为 ,所以 ,所以 。
而 已经是树的一条直径,是树中所能得到的两点间的最长距离。所以 也是树的直径。因此 是某条直径的端点。
从直径的一个端点出发,找到的最远点就是直径的另一个端点。
情况2
点 位于 和 之间,包括 。
,所以 ,所以 。因此,可以得知 也是图的一条直径。
该性质的成立前提是边权均为非负。
在情况 1 中,我们使用了 ,。这两个不等式成立的前提是 。负权边会破坏这里的前提。
性质2
直径的两个端点一定是叶子
如果 不是叶子,那么除了通向 的方向以外还可以继续延伸;由于边权为正,就能得到一条比 更长的路径,矛盾。
注意边权为正这一点。以及若根节点的度数为 ,那么根节点也有可能作为直径的一个端点出现。
性质3
任意点的最远点,一定可以在一条固定直径的两个端点中找到。
设从 出发,所能到达的最远点为 。
设 是 到直径 的接入点, 是 到直径 的接入点。
情况1
的接入点不同
此时因为 ,所以 。因此,将 换成 ,结果不会更差。
情况2
接入点是同一个点,甚至 和 接入到直径 之前就已经合并。
无论哪种情况,,因此,。
性质4
定义树上节点 的偏心距 为从 出发,所能到达最远节点的距离。
树的中心是指偏心距最小的节点。
所有直径一定经过树的中心。
性质5
一棵树上,一定存在一个点 被这棵树的所有直径经过。
性质6
任意两条直径一定有且仅有一个极长连续段(可以只是一个点)重合。若两条直径共用一个端点,这个端点也算重合。
树形 DP 求树的直径
设树根为 号节点, 表示从节点 出发走向以 为根的子树,能够到达的最远节点的距离。
设 的子节点为 ,那么
设 表示子树 中,经过 的最长链的长度。
那么,以 为端点,向其子树出发的链中,选择最长的两条合在一起便是在子树 中, 经过 的最长链。
因此
在实际实现的过程中,我们没必要分别枚举 来计算答案。在依次枚举 的子节点的过程中,令 保存当前所得到的 的最大值,若当前节点为 ,通过 所能得到的最长链为 ,经过当前节点以后, 更新为 。
void dp(int x) {
v[x] = 1;
for (auto [y, z]: e[x]) {
if (v[y]) continue;
dp(y);
ans = max(ans, d[x] + d[y] + z);
d[x] = max(d[x], d[y] + z);
}
}
两次 BFS 求树的直径
- 从任意一个节点出发,通过
BFS或DFS对树进行一次遍历,求出与出发点距离最远的节点,记为 。 - 从节点 出发,通过
BFS或DFS对树再进行一次遍历,求出与 距离最远的节点,记为 。
从 到 的路径就是树的一条直径。