树上的最远距离

题目

题目描述

给定一棵树,对于每一个点,输出离它最远的点到它的距离。

输入格式

第一行包含整数 \(n\)

接下来 \(n-1\) 行,每行包含两个整数 \(a_i,b_i\),表示点 \(a_i\)\(b_i\) 之间存在一条边。

输出格式

输出一行 \(n\) 个整数,第 \(i\) 个数表示离节点 \(i\) 最远的点到它的距离。

样例

输入数据#1

8
1 5
1 4
6 3
2 6
6 1
3 7
4 8

输出数据#2

3 4 4 4 4 3 5 5

数据范围

  • 对于 \(20\%\) 的数据:\(1≤n≤10\)
  • 对于 \(60\%\) 的数据:\(1≤n≤10^3\)
  • 对于 \(100\%\) 的数据:\(1≤n≤10^5\)\(1≤a_i,b_i≤n\)

思路分析

树的直径好题。

我们如果对每个点都跑一遍树的直径 dfs,时间复杂度 \(O(n^2)\),超时。数据范围 \(n\leq10^5\) 显然只允许我们跑一遍树的直径 dfs。

其实,我们只需先找到一条直径,然后对于每个点,比较两个端点哪个距离该点较远即可。

现在求点 \(u\) 最远的点到它的距离,记为 \(ans\),跑树的直径 dfs 后得到直径 \(a\to b\),分类讨论:

若点 \(u\) 不在直径 \(a\to b\) 的路径上,如下图:

反证法:

假设有点 \(v\) 是点 \(u\) 最远的点,距离为 \(s_2+s_4\),直径的端点 \(a\) 到点 \(u\) 的距离为 \(s_2+s_3\),则:

\[s_2+s_4>s_2+s_3 \]

解得 \(s_4>s_3\)

所以 \(x\to v\) 的路径比 \(x\to a\) 的路径更长,直径则应为 \(s_1+s_4\),与树的直径是 \(a\to b\) 矛盾,显然不存在。

若点 \(u\) 在直径 \(a\to b\) 的路径上:

证明如上,其中 \(s_2=0\),显然也不存在。

证毕。

\(\texttt{code}\)

/*Written by smx*/
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define QAQ cout<<"QAQ\n";
const int MAXN=1e5+5,inf=1e18,mod=1e9+7;
int n;
vector<int> g[MAXN];
int dist[MAXN],d[MAXN];
int dfs(int now,int fa){
	int ans=now;
	for(auto x:g[now]){
		if(x==fa){
			continue;
		}
		dist[x]=dist[now]+1;
		int d=dfs(x,now);
		if(dist[d]>dist[ans]){
			ans=d;
		}
	}
	return ans;
}
signed main(){
	//freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>n;
	for(int i=1;i<n;i++){
		int u,v;
		cin>>u>>v;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	int u,v;
	u=dfs(1,0);
	dist[u]=0;
	v=dfs(u,0);
	for(int i=1;i<=n;i++){
		d[i]=dist[i];
	}
	dist[v]=0;
	dfs(v,0);
	for(int i=1;i<=n;i++){
		cout<<max(d[i],dist[i])<<" ";
	}
	return 0;
}
posted @ 2024-12-03 22:56  SMall_X  阅读(80)  评论(0)    收藏  举报