Noip2013 货车运输

思路

       观察题目,经过思考,发现两个点之间最小值最大的路径一定在最大生成树上。 所以我们可以先用kruskal算法求出最大生成树。 之后问题转化为链上最小值。 首先我们可以以1为根建树,之后用倍增算法。dadi,j表示i的2j的父亲,maxi,j表示i到他的2j的父亲路径上所有边的最小权值。 这样我们在求lca的同时可以求出链上最小值。 时间复杂度O(mlogm),空间复杂度O(nlogn)。

代码

//by ziwan Catherine
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define inf 999999999
using namespace std;
int n,m,cnt,head[10005],q;
bool v[10005];
int f[10005],deep[10005];
int fa[10005][21],w[10005][21];
struct edge{
	int x,y,dis;
}e[100005];
struct edge2{
	int to,next,w;
}ed[100005];
bool cmp(edge x,edge y){
	return x.dis>y.dis;
}
void add(int x,int y,int w){//存储最长路的图 
	ed[++cnt].to=y;
	ed[cnt].next=head[x];
	ed[cnt].w=w;
	head[x]=cnt;
}
int find(int x){
	 if(f[x]==x)  return f[x];
	 return f[x]=find(f[x]);
   
} 
void kruskal(){
	sort(e+1,e+m+1,cmp);
	for(int i=1;i<=n;i++)
	f[i]=i;//并查集初始化
	for(int i=1;i<=m;i++)
		if(find(e[i].x)!=find(e[i].y)){
			int X=find(e[i].x),Y=find(e[i].y);
			f[X]=Y;
			add(e[i].x,e[i].y,e[i].dis);
			add(e[i].y,e[i].x,e[i].dis);//无向图 
		}
}
void dfs(int x){
	v[x]=1;
	for(int i=head[x];i;i=ed[i].next){
		int y=ed[i].to;
		if(v[y]) continue;
		deep[y]=deep[x]+1;
		fa[y][0]=x;
		w[y][0]=ed[i].w;
		dfs(y);
	}
}
int do_lca(int x,int y){
	if(find(x)!=find(y)) return -1;//不连通
	int ans=inf;
	if(deep[x]>deep[y]) swap(x,y);//保证y节点更深
	for(int i=20;i>=0;i--)//将y提到x节点相同深度  【注意是>=0】
		if(deep[fa[y][i]]>=deep[x]){
			ans=min(ans,w[y][i]);//更新最大载重(最小边权)
			y=fa[y][i];//y向上提 
		}
	if(x==y) return ans;
	for(int i=20;i>=0;i--) //寻找公共祖先   【注意是>=0】
		if(fa[x][i]!=fa[y][i]){
			ans=min(ans,min(w[x][i],w[y][i]));
			x=fa[x][i];
			y=fa[y][i];
		}
	ans=min(ans, min(w[x][0], w[y][0]));
	//更新此时x,y到公共祖先最大载重,fa[x][0], fa[y][0]即为公共祖先 
	return ans;
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++)
		cin>>e[i].x>>e[i].y>>e[i].dis;
	kruskal();
	for(int i=1;i<=n;i++)
		if(!v[i]){//从每一个根节点进行搜索求出节点深度
			deep[i]=1;
			dfs(i);
			fa[i][0]=i;
			w[i][0]=inf;
		}
	for(int i=1;i<=20;i++)//LCA初始化 
		for(int j=1;j<=n;j++){
			fa[j][i]=fa[fa[j][i-1]][i-1];
			w[j][i]=min(w[j][i-1],w[fa[j][i-1]][i-1]);
		}
	cin>>q;
	for(int i=1;i<=q;i++){
		int a,b;
		cin>>a>>b;
		cout<<do_lca(a,b)<<endl;
	} 
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值