树中所有简单路径的最大值即为树的直径,可以通过两次 DFS 或者树形 DP 在O(n) 时间内求解。
证明 假设结点 s′ 和 t′ 之间唯一的简单路径为树的直径,(a,b) 表示结点a 和 b之间的唯一的简单路径的长度。若该最远点 s 不是树的直径的一个端点,则对于当前根结点 u 来说:
\begin{array}{c} (s',t') \leq (s',u)+(u,t') \leq (s,u)+(u,t') = (s,t') \end{array}
与 (s′,t′)为直径而 s不是直径的端点矛盾。
综上可知,s 一定是树的直径的一个端点。 推论:树中任意结点的最远点要么是 s,要么是t((s,t)为树的直径)。 证明同上,略。
\begin{array}{c} L_{1rt} + L_{2nd} \end{array}
子树 v 的高度等于
\begin{array}{c} \max\{L_k = h_k + l_k\} \end{array}
其中,k 取遍 v的所有子树,L_{1rt} 和 L_{2nd}即对应子树 v 的高度和次高度。
【注】以下模板均是基于无权树求树的直径,有权树只需在前向星中记录一下边权,然后更新结点高度/深度不是 + 1 而是 + 对应边权即可。
#include <bits/stdc++.h>
using namespace std;
#ifndef _DIAMETEROFTREE_
#define _DIAMETEROFTREE_
#define ll int
#define MAXN 10005
// 树的直径
struct DiameterOfTree{
// 前向星存边
ll cnt; // 动态开点
ll head[MAXN]; // 第一条出边
struct Edge{
ll to; // 边的终点
ll next; // 下一条边
}e[MAXN<<1]; // 存取双向边
// 初始化前向星
void init() {
cnt = 0;
memset(head, -1, sizeof(head));
}
// 添加双向边
void addEdge(ll u, ll v) {
// 存取双向边
e[cnt].next = head[u];
e[cnt].to = v;
head[u] = cnt++;
e[cnt].next = head[v];
e[cnt].to = u;
head[v] = cnt++;
}
// DFS 求深度最大的结点
ll ans; // 最大深度结点
ll vis[MAXN]; // 标记数组
ll depth[MAXN]; // 深度数组
void dfs(ll u) {
vis[u] = 1;
for(ll i = head[u]; i != -1; i = e[i].next) {
ll v = e[i].to;
if(!vis[v]) {
depth[v] = depth[u] + 1;
if(depth[v] > depth[ans]) {
ans = v;
}
dfs(v);
}
}
}
// 求解树的直径
ll getDiameter(ll u) {
// 第一遍 DFS
ans = u;
memset(vis, 0, sizeof(vis));
memset(depth, 0, sizeof(depth));
dfs(u);
// 第二边 DFS
memset(vis, 0, sizeof(vis));
memset(depth, 0, sizeof(depth));
dfs(ans);
return depth[ans];
}
};
#endif
#include <bits/stdc++.h>
using namespace std;
#ifndef _DIAMETEROFTREE_
#define _DIAMETEROFTREE_
#define ll int
#define MAXN 10005
// 树的直径
struct DiameterOfTree{
// 前向星存边
ll cnt; // 动态开点
ll head[MAXN]; // 第一条出边
struct Edge{
ll to; // 边的终点
ll next; // 下一条边
}e[MAXN<<1]; // 存取双向边
// 初始化前向星
void init() {
cnt = 0;
memset(head, -1, sizeof(head));
}
// 添加双向边
void addEdge(ll u, ll v) {
// 存取双向边
e[cnt].next = head[u];
e[cnt].to = v;
head[u] = cnt++;
e[cnt].next = head[v];
e[cnt].to = u;
head[v] = cnt++;
}
// DFS 树形 dp
ll diameter; // 树的直径
ll vis[MAXN]; // 标记数组
ll dfs(ll u) {
vis[u] = 1;
ll h1 = 0, h2 = 0;
for(ll i = head[u]; i != -1; i = e[i].next) {
ll v = e[i].to;
if(!vis[v]) {
ll h = dfs(v) + 1;
if(h > h1) {
h2 = h1;
h1 = h;
} else if(h > h2) {
h2 = h;
}
}
}
diameter = max(diameter, h1+h2);
return h1;
}
// 求解树的直径
ll getDiameter(ll u) {
diameter = 0;
memset(vis, 0, sizeof(vis));
dfs(u);
return diameter;
}
};
#endif