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

19 篇文章 0 订阅
2 篇文章 0 订阅

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<n,1<=L,w<=1000。

Solution

对于每个点我们求出其“树心”,这个定义树心是到树上最远距离最小的节点,那么所有的树心连成一个菊花图就是最优解。

同时,这个菊花图的中心是最大的到树上最远点的“树心”,直接构造连边找直径即可。注意对最远距离中父亲节点以上的点。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#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||now<mx){rt=x;mx=now;}
}
inline void dfs(I x,I y,I z){
	if(z>ans){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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值