1.题意
实验室里原先有一台电脑(编号为1),最近氪金带师咕咕东又为实验室购置了N-1台电脑,编号为2到N。每台电脑都用网线连接到一台先前安装的电脑上。但是咕咕东担心网速太慢,他希望知道第i台电脑到其他电脑的最大网线长度,但是可怜的咕咕东在不久前刚刚遭受了宇宙射线的降智打击,请你帮帮他。
提示: 样例输入对应这个图,从这个图中你可以看出,距离1号电脑最远的电脑是4号电脑,他们之间的距离是3。 4号电脑与5号电脑都是距离2号电脑最远的点,故其答案是2。5号电脑距离3号电脑最远,故对于3号电脑来说它的答案是3。同样的我们可以计算出4号电脑和5号电脑的答案是4.
2.样例
Input
输入文件包含多组测试数据。对于每组测试数据,第一行一个整数N (N<=10000),接下来有N-1行,每一行两个数,对于第i行的两个数,它们表示与i号电脑连接的电脑编号以及它们之间网线的长度。网线的总长度不会超过10^9,每个数之间用一个空格隔开。
Output
对于每组测试数据输出N行,第i行表示i号电脑的答案 (1<=i<=N).
Sample Input
5
1 1
2 1
3 1
1 1
Sample Output
3
2
3
4
4
3.解题思路
- 该题大意就是对于一棵n个结点的无根树,求出每个结点到最远点的距离,要求时间复杂度为O(n)
- 该题转化为,对于一个点距离它最远的点一定是直径的一个端点,找树的直径的端点,方法:两次dfs即先从任意一点v1出发,找到距它最远的点v2,再从v2出发,找到距它最远的点v3,则v2,v3为直径的两个端点,v2到v3的距离为直径
证明:
(1)若v1为直径上的一点,则v2也在直径上且为直径的端点
(2)若v1不在直径上,用反证法证明,设此时v2v3不是直径,vu是直径。
v2v3与vu有交点o(因为电脑间是连接的且直径最长),因为v2距v3最远,所以v2o+ov3>v2o+ov,所以ov3>ov,则uo+ov3>uo+ov=uv,所以uv不是直径,即v2为直径的一个端点
(3)综上,从任意一点v1出发,找到距它最远的点v2,则v2为直径的一个端点,同一方法可以得到直径的另一个端点
- 用dis记录路径长度,用l_p记录最后一个端点,当路径更长时更新l_p,注意每次调用dfs要清空dis,l_p,vis,在每组数据完成后清空vector。
5.AC代码
#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
const int M=1e4+5;
struct edg{
int v;
long long w;
edg(int a,long long b){
v=a;
w=b;
}
};
vector<edg>e[M];
long long max_dis[M][2];
long long dis;
int l_p;
int vis[M];
void dfs(int u,long long ds,int f)
{
vis[u]=1;
max_dis[u][f]=ds;
if(ds>dis)
{
dis=ds;
l_p=u;
}
for(int i=0;i<e[u].size();i++)
{
int v=e[u][i].v;
long long w=e[u][i].w;
if(vis[v]==0)
{
dfs(v,ds+w,f);
}
}
}
int main()
{
int n;
while(~scanf("%d",&n))
{
int v1,l_p1=1,l_p2=1;
long long w1;
memset(vis, 0, sizeof(vis));
for(int i=0;i<M;i++)
{
for(int j=0;j<2;j++)
{
max_dis[i][j]=0;
}
}
for(int i=2;i<n+1;i++)
{
cin>>v1>>w1;
e[i].push_back(edg(v1,w1));
e[v1].push_back(edg(i,w1));
}
l_p=0;
dis=0;
dfs(1,0,0);
l_p1=l_p;
memset(vis, 0, sizeof(vis));
l_p=0;
dis=0;
dfs(l_p1,0,0);
l_p2=l_p;
memset(vis, 0, sizeof(vis));
dfs(l_p2,0,1);
for(int i=1;i<=n;i++)
{
cout<<max(max_dis[i][0],max_dis[i][1])<<endl;
}
for(int i=0;i<M;i++)
{
while(!e[i].empty())
{
e[i].clear();
}
}
}
return 0;
}