Description
你有一个n个点m条边的森林,编号从0开始,边有边权,你现在要添加若干边权为L的边,满足:
1、最后n个点构成一颗树。
2、这棵树的直径尽量小。
请你求出这个最小的直径是多少。
Input
第一行三个整数n,m,L。
接下来m行,每行三个整数u,v,w,表示u和v之间有长为w的边。
Output
一行一个整数,表示最小直径的长度。
Sample Input
12 8 2
0 8 4
8 2 2
2 7 4
5 11 3
5 1 7
1 3 1
1 9 5
10 6 3
Sample Output
18
Data Constraint
对于20%的数据:n<=5;
对于60%的数据:n<=2000;
对于100%的数据:n<=50000,m
Solution
对于每个点我们求出其“树心”,这个定义树心是到树上最远距离最小的节点,那么所有的树心连成一个菊花图就是最优解。
同时,这个菊花图的中心是最大的到树上最远点的“树心”,直接构造连边找直径即可。注意对最远距离中父亲节点以上的点。
Code
#include
#include
#include
#include
#define I int
#define F(i,a,b) for(I i=a;i<=b;i++)
#define Fd(i,a,b) for(I i=a;i>=b;i--)
#define mem(a,b) memset(a,b,sizeof(a))
#define N 50010
using namespace std;
void rd(I &x){
x=0;I w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
x*=w;
}
I n,m,l,x,y,z,t[N*2],nx[N*2],ls[N],w[N*2],bz[N],rt,f[N],g[N],tot,mx,cnt,ans;
struct node{I v,x;}a[N];
struct T{I v,k;}b[N];
inline void add(I x,I y,I z){t[++tot]=y,nx[tot]=ls[x],ls[x]=tot,w[tot]=z;}
inline void dg1(I x,I y){
bz[x]=1;I st;
for(I k=ls[x];k;k=nx[k]) if(t[k]!=y){
dg1(t[k],x);
f[x]=max(f[x],st=f[t[k]]+w[k]);
if(st>b[x].v){g[x]=b[x].v,b[x].v=st,b[x].k=t[k];}
else g[x]=st>g[x]?st:g[x];
}
}
inline void dg(I x,I y,I z){
for(I k=ls[x];k;k=nx[k]) if(t[k]!=y){
if(b[x].k!=t[k]) dg(t[k],x,max(z,b[x].v)+w[k]);
else dg(t[k],x,max(z,g[x])+w[k]);
}
I now=max(f[x],z);
if(!rt||nowans){ans=z;rt=x;}
for(I k=ls[x];k;k=nx[k]) if(t[k]!=y){dfs(t[k],x,z+w[k]);}
}
I cmp(node x,node y){return x.v>y.v;}
I main(){
freopen("diameter.in","r",stdin);
freopen("diameter.out","w",stdout);
rd(n),rd(m),rd(l);
F(i,1,m){
rd(x),rd(y),rd(z);x++,y++;
add(x,y,z),add(y,x,z);
}
F(i,1,n) if(!bz[i]){
rt=mx=0;
dg1(i,0),dg(i,0,0);
a[++cnt]=node{mx,rt};
}
sort(a+1,a+1+cnt,cmp);
F(i,2,cnt){
add(a[i].x,a[1].x,l);
add(a[1].x,a[i].x,l);
}
dfs(1,ans=0,0);
dfs(rt,ans=0,0);
printf("%lld\n",ans);
return 0;
}