IOI 2013 Deaming

5 篇文章 0 订阅

Description:

n n n个点 m m m条边的图,现在加 n − 1 − m n-1-m n1m条长度为 L e n Len Len的边使之变为一棵树,求树的直径的最小值。
n ≤ 1 0 5 n\le10^5 n105

Solution:

  • 比较裸的题了…
  • 初始,我们预处理出每个树的直径,求出直径上最佳的中点 m i d mid mid( d = m i n { m a x { d i s [ m i d ] , d i s [ R ] − d i s [ m i d ] } } d=min\{max\{dis[mid],dis[R]-dis[mid]\}\} d=min{max{dis[mid],dis[R]dis[mid]}}),拿这些中点来连边。
  • 最优的连边策略,一定是拿拥有最长的 d d d的中点与其它中点建边。
  • 建完边后再求一遍直径即为答案。

Code:

#include<bits/stdc++.h>
using namespace std;
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t) for(int i=(f),i##_end_=(t);i<i##_end_;++i)
#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;--i)
#define ll long long
template<class T>inline bool chkmin(T&x,T y){return x>y?x=y,1:0;}
template<class T>inline bool chkmax(T&x,T y){return x<y?x=y,1:0;}
template<class T>inline void rd(T&x){
	x=0;char c;
	while((c=getchar())<48);
	do x=(x<<1)+(x<<3)+(c^48);
	while((c=getchar())>47);
}

const int N=1e5+2,INF=0x3f3f3f3f;

int n,m;
ll Len;

int qwq,head[N];
struct edge{
	int to,nxt;
	ll w;
}E[N<<1];
void addedge(int x,int y,ll z){E[qwq]=(edge){y,head[x],z};head[x]=qwq++;}
#define EREP(x) for(int i=head[x];~i;i=E[i].nxt)

struct p100{
	
	ll dis[N];
	int fa[N];
	ll Mn[N];// 每个小树的 min{max{dis[L][mid],dis[mid][R]}} 
	int rt[N],cnt;// 每个小树的中点 
	
	bool vis[N];
	
	int Id;
	ll Mx;
	
	void dfs1(int x,int f,ll d){
		if(chkmax(Mx,d)) Id=x;
		vis[x]=1;
		EREP(x){
			int y=E[i].to;
			if(y==f)continue;
			dfs1(y,x,d+E[i].w);
		}
	}
	
	void dfs2(int x,int f,ll d){
		if(chkmax(Mx,d)) Id=x;
		fa[x]=f;
		dis[x]=d;
		EREP(x){
			int y=E[i].to;
			if(y==f)continue;
			dfs2(y,x,d+E[i].w);
		}
	}
	
	void solve(){
		REP(i,1,n) if(!vis[i]) {
			Id=Mx=-1;
			dfs1(i,0,0);
			rt[++cnt]=Id;//L
			Mx=-1;
			dfs2(Id,0,0);//R
			int R=Id,L=rt[cnt],mid=R;
			Mn[cnt]=INF;
			if(L==R) Mn[cnt]=0;
			while(mid!=L){
				if(chkmin(Mn[cnt],max(dis[mid],dis[R]-dis[mid])))rt[cnt]=mid;
				mid=fa[mid];
			}
		}
		
		ll mx1=-1;
		int Rt1=-1;
		
		REP(i,1,cnt)if(chkmax(mx1,Mn[i])) Rt1=rt[i];
		
		REP(i,1,cnt){
			if(Rt1==rt[i])continue;
			addedge(Rt1,rt[i],Len);
			addedge(rt[i],Rt1,Len);
		}
		
		Mx=Id=-1;
		dfs1(1,0,0);
		Mx=-1;
		dfs2(Id,0,0);
		printf("%lld\n",Mx);
		
	}
}p2;

int main(){
//	freopen("dreaming.in","r",stdin);
//	freopen("dreaming.out","w",stdout);
	rd(n),rd(m),rd(Len);
	memset(head,-1,sizeof head);
	REP(i,1,m){
		int a,b,c;
		rd(a),rd(b),rd(c);
		++a,++b;
		addedge(a,b,c);
		addedge(b,a,c);
	}
	
	p2.solve();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值