5042. 【NOI2017模拟4.4】最小直径

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;
}

 

你可能感兴趣的:(题目,树的直径,dfs,树的直径,树的直径)