树上的最远距离

题目

题目描述

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

输入格式

第一行包含整数 n n n

接下来 n − 1 n-1 n1 行,每行包含两个整数 a i , b i a_i,b_i ai,bi,表示点 a i a_i ai b i b_i bi 之间存在一条边。

输出格式

输出一行 n n n 个整数,第 i i i 个数表示离节点 i 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 % 20\% 20% 的数据: 1 ≤ n ≤ 10 1≤n≤10 1n10
  • 对于 60 % 60\% 60% 的数据: 1 ≤ n ≤ 1 0 3 1≤n≤10^3 1n103
  • 对于 100 % 100\% 100% 的数据: 1 ≤ n ≤ 1 0 5 1≤n≤10^5 1n105 1 ≤ a i , b i ≤ n 1≤a_i,b_i≤n 1ai,bin

思路分析

树的直径好题。

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

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

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

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

反证法:

假设有点 v v v 是点 u u u 最远的点,距离为 s 2 + s 4 s_2+s_4 s2+s4,直径的端点 a a a 到点 u u u 的距离为 s 2 + s 3 s_2+s_3 s2+s3,则:

s 2 + s 4 > s 2 + s 3 s_2+s_4>s_2+s_3 s2+s4>s2+s3

解得 s 4 > s 3 s_4>s_3 s4>s3

所以 x → v x\to v xv 的路径比 x → a x\to a xa 的路径更长,直径则应为 s 1 + s 4 s_1+s_4 s1+s4,与树的直径是 a → b a\to b ab 矛盾,显然不存在。

若点 u u u 在直径 a → b a\to b ab 的路径上:

证明如上,其中 s 2 = 0 s_2=0 s2=0,显然也不存在。

证毕。

code \texttt{code} 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值