2017湘潭大学邀请赛H题(树的直径)
链接:https://www.icpc.camp/contests/4mYguiUR8k0GKE
H. Highway
The input contains zero or more test cases and is terminated by end-of-file. For each test case: The first line contains an integer n. The i-th of the following (n − 1) lines contains three integers ai , bi and ci
. • 1 ≤ n ≤ 105
• 1 ≤ ai , bi ≤ n
• 1 ≤ ci ≤ 108
• The number of test cases does not exceed 10.
题意:
每次连接最远的两点,直到所有点都相通。
最多有n-1条边
题解:
如何每次都找到最远的两个点呢?
我们需要用到一个定理:树上的任何一个点的最远点一定会是<树的直径>中的一个。
树的直径指:树上的最远的两个点
接下来我们证明这个定理——
1,利用这个定理,我们可以从<1节点>dfs找到一个直径上的点。
2,用直径上的这个点dfs可以找到另外一个直径上的点。
3,找出所有点到这两个直径上的点的距离
4,将所有点都连接在直径的两个点之一,就是答案了
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+5; int f[maxn],nex[2*maxn],w[2*maxn],tob[2*maxn],inde; long long dis1[maxn],dis2[maxn]; bool vis[maxn]; long long s1,maxdis,s2; long long madis=-1; void add(int a,int b,int wn) { inde++; tob[inde]=b; w[inde]=wn; nex[inde]=f[a]; f[a]=inde; } void dfs1(int x,long long v) { vis[x]=0; for(int i=f[x];i;i=nex[i]) { if(vis[tob[i]]) { long long gg=v+w[i]; if(gg>madis) { madis=gg; s1=tob[i]; } dfs1(tob[i],gg); } } } void dfs2(int x,long long v) { vis[x]=0; for(int i=f[x];i;i=nex[i]) { if(vis[tob[i]]) { long long gg=v+w[i]; if(gg>maxdis) { maxdis=gg; s2=tob[i]; } dis1[tob[i]]=gg; dfs2(tob[i],gg); } } } void dfs3(int x,long long v) { vis[x]=0; for(int i=f[x];i;i=nex[i]) { if(vis[tob[i]]) { long long gg=v+w[i]; dis2[tob[i]]=gg; dfs3(tob[i],gg); } } } int main() { int n; while(cin>>n) { inde=0; for(int i=1;i<=n;i++)f[i]=0; for(int i=1;i<n;i++) { int a,b,wn; scanf("%d %d %d",&a,&b,&wn); add(a,b,wn); add(b,a,wn); } maxdis=madis=-1; for(int i=1;i<=n;i++)vis[i]=1; dfs1(1,0); for(int i=1;i<=n;i++)vis[i]=1; dfs2(s1,0); for(int i=1;i<=n;i++)vis[i]=1; dfs3(s2,0); long long ans=maxdis; for(int i=1;i<=n;i++) { if(i==s1||i==s2)continue; ans+=max(dis1[i],dis2[i]); } cout<<ans<<endl; } return 0; }