树的直径和证明

(以下阐述部分借鉴)
博客1
博客2

概念

树的直径:2点距离最远的路径.

结论

先说结论,对于一颗无根树,首先随便找一个点(u)开始进行搜索,找到离当前点最远的一点(s),然后从(s)开始搜最远的点(t)树的直径就为(s-t)

Ⅰ两遍(dfs||bfs)

证明
找到直径,我们只需找到直径2个端点其中的一个,然后找到离当前点最远的点即为另一个端点。
1.如果(u)(s-t)的路径上,很明显是对的,到达(u)最远的点必为(s)(t),因为是从(u)开始搜最远的点的。

2.如果(u)不在(s-t)的路径上,那么从(u)搜最远的点必然与(s-t)相交。

如图,如果u搜到最远的点为T,那么(dis(u,t) > dis(u,x) + dis(x,t))
很显然: (dis(s,t ) = dis(s,x) + dis(x,t) < dis(s,T) = dis(s,x) + dis(u,x) + dis(u,T))

所以 很容易得出必与直径相交,且搜到的点为另一个端点。

Ⅱ树型DP

设f[i]表示以i为根,到其子树的叶节点的最大距离。

考虑如何用子节点更新父节点,
当前点到叶节点的最大距离=(max){子节点到叶节点的距离+当前点到子节点的距离}。

设u为当前节点,v为u的子节点,(dis(u,v))是从u−>v这条路径上的距离
得到转移方程:

(f[u]=max){(f[v]+dis(u,v))}
如何维护以u为根的子树中的直径呢
以u为根子树的直径(=max){u到叶节点的最大距离+子节点到叶节点的最大距离+u到叶节点的距离}
然后我们钦定一个节点为根,比如1
得到转移方程:

(ans=max){(f[u]+f[v]+dis(u,v))}
ans即为树的直径
需要注意的是,我们要在更新f[u]之前更新ans,因为从u经过v到叶节点的路径是最长的路径,这样这条路径会被更新两次

这样做一定会选出u到叶节点最长的两条路径

void dp(int now)
{
	vis[now]=1;
	for(int i=head[now];i;i=d[i].nxt)
	{
		int v=d[i].to;
		if(vis[v])	continue;
		dp(v);
		ans=max(ans,dis[now]+dis[v]+d[i].w);
		dis[now]=max(dis[now],dis[v]+d[i].w);
	}
}
原文地址:https://www.cnblogs.com/iss-ue/p/12546663.html