NOIP 2013 提高组 货车运输

题面

简要题解

最大生成树+LCA

简要做法:
求出最大生成树,可以证明汽车一定要在最大生成树上跑是最优的
然后用倍增LCA求出货车要跑的点对(x, y)路径上的最小边权

证明:
由于每条路上有限制重量,我们的目标是让每条路径的最小边权最大

倘若货车走的是非最大生成树上的边,设该条边权为 w w w,连接 ( x , y ) (x, y) (x,y), 则由于最大生成树的性质,树上路径 ( x , y ) (x, y) (x,y)任意一条边的限重都大于 w w w,均会更优

Code
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define repd(i,a,b) for(int i=a;i>=b;i--)
#define _(d) while(d(isdigit(ch=getchar())))
#define inf 0x7ffff
template <class T> void g(T&t) {T x,f=1;char ch;_(!)ch=='-'?f=-1:f;x=ch-48;_()x=x*10+ch-48;t=f*x;}
const int N=2e5+4;
int fa[N],F[N][23],fa1[N][23],n,m,tot,d[N];
struct E{int to,nxt,lim;}e[N<<1];
int head[N];
void ins(int x,int y,int z){
	e[++tot]=E{y,head[x],z},head[x]=tot;
}
struct Edge{int x,y,w;}ee[N]; 
int getfa(int x){return x==fa[x]?x:fa[x]=getfa(fa[x]);}
bool cmp(Edge a,Edge b){return a.w>b.w;}
bool vis[N];
void dfs(int x){
	if(vis[x]) return; vis[x]=1;
	for(int i=head[x];i;i=e[i].nxt){ 
		if(vis[e[i].to]) continue;
		F[e[i].to][0]=e[i].lim; 
		fa1[e[i].to][0]=x;
		d[e[i].to]=d[x]+1;
		dfs(e[i].to);
	}
}
void work(int x,int y){
	int ans=1e9;
	if(d[x]<d[y]) swap(x,y);
	repd(i,20,0) if(d[fa1[x][i]]>=d[y]) ans=min(ans,F[x][i]),x=fa1[x][i];
	if(x==y) {
		printf("%d\n",ans);
		return ;
	}
	repd(i,20,0){
		if(fa1[x][i]!=fa1[y][i]) {
			ans=min(ans,F[x][i]);
			ans=min(ans,F[y][i]);
			x=fa1[x][i],y=fa1[y][i];	
		}
	}
	ans=min(ans,min(F[x][0],F[y][0]));
	printf("%d\n",ans);
}
int main(){
	g(n),g(m);
	memset(F,1,sizeof(F)); 
	rep(i,1,n) fa[i]=i;
	rep(i,1,m) g(ee[i].x),g(ee[i].y),g(ee[i].w); 
	sort(ee+1,ee+1+m,cmp);
	rep(i,1,m){
		int f1=getfa(ee[i].x),f2=getfa(ee[i].y);
		if(f1!=f2){
			fa[f1]=f2;
			ins(ee[i].x,ee[i].y,ee[i].w);
			ins(ee[i].y,ee[i].x,ee[i].w);
		}
	}
	rep(i,1,n) if(!vis[i]) d[i]=1,dfs(i),fa1[i][0]=i,F[i][0]=inf;
	rep(j,1,20)
		rep(i,1,n) fa1[i][j]=fa1[fa1[i][j-1]][j-1];

	rep(j,1,20) rep(i,1,n){
		F[i][j]=min(F[i][j],min(F[i][j-1],F[fa1[i][j-1]][j-1]));
	}
	int Q;g(Q);
	while(Q--){
		int x,y;g(x),g(y);
		if(getfa(x)!=getfa(y)){printf("-1\n");continue;}
		work(x,y);
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

可爱の小公举

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值