树的直径最简单的做法:随便找一个的点dfs找离他最远的点p,然后再一遍dfs找距离p最远的点q。p-q即直径。
下面给出简单的小证明:
假设第一遍dfs找到了x,直径是u-v。
那么离x最远的点要么是y,z,b,a这几个点中的一个。(分别对应几种情况)
显然y和z,a不可能,因为dis(x,v)一定大于dix(y,x),dis(y,z),dis(x,a),否则,我们从u出发,离u的最远点就不是v了。
对于b,也不可能,同上,dis(x,u)一定大于dis(x,b)否则,u-v就不是直径了。
所以证明成功。
对于这一题:
我们可以先求出两个直径的端点:p,q
然后每个点查询max(dis(p,i),dis(q,i))即可。
#include <iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int M = 1e4+7;
int head[M],cnt;
struct EDGE{int to,nxt,w;}ee[M*2];
void add(int x,int y,int w){ee[++cnt].nxt=head[x],ee[cnt].w=w,ee[cnt].to=y,head[x]=cnt;}
int mx=-1;
int p,q;//直径的两端点
int dp[M],dq[M];
void dfs(int x,int fa,int dep)
{
if(dep>mx)mx=dep,p=x;
for(int i=head[x];i;i=ee[i].nxt)
{
int y=ee[i].to,w=ee[i].w;
if(y==fa)continue;
dfs(y,x,dep+w);
}
}
void gao(int x,int fa,int k)
{
for(int i=head[x];i;i=ee[i].nxt)
{
int y=ee[i].to,w=ee[i].w;
if(y==fa)continue;
if(k)dp[y]=dp[x]+w;//x点到p的距离
else dq[y]=dq[x]+w;
gao(y,x,k);
}
}
int main()
{
int n;
while(~scanf("%d",&n))
{
memset(head,0,sizeof(head));
memset(dp,0,sizeof(dp));
memset(dq,0,sizeof(dq));
cnt=0;
for(int i=2;i<=n;i++)
{
int u,v,w;
scanf("%d%d",&v,&w);
add(i,v,w);
add(v,i,w);
}
mx=-1;dfs(1,0,0);q=p;
mx=-1;dfs(q,0,0);
gao(p,0,1);
gao(q,0,0);
for(int i=1;i<=n;i++)
{
int tp=max(dp[i],dq[i]);
printf("%d\n",tp);
}
}
return 0;
}