【NOIP2013提高组 day1】货车运输

在这里插入图片描述

分析

先求最大生成树,把一些多余的边去掉
最近公共祖先LCA 可用树上倍增的方法 (Tarjan好像也行不知道会不会超时
询问前处理
w[i][j]=min(w[f[i][j-1]][j-1],w[i][j-1])
最后对于询问的x,y返回ans
ans=min(ans,w[i][j]) (w[i][j]在x,y到lca(x,y)路径上)
可能有一点点玄学,具体看代码就清楚 更玄学 啦~

代码如下

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=10005,inf=0x3f3f3f3f;
struct E{
	int x,y,val;
}edge[50005];
struct tree{
	int ver,to,w;
}edge2[100050];
int n,m,tot=0,p,head[N],t;
int fa[N],f[N][21],w[N][21],d[N];
int read(){
	int sum=0,f=1;
    char ch=getchar();
    while(ch>'9'||ch<'0'){
    	if(ch=='-')f=-1;
    	ch=getchar();
	}
	 while(ch>='0'&&ch<='9'){
    	sum=(sum<<3)+(sum<<1)+ch-'0';
		ch=getchar();
	}
	return sum*f;
}
bool comp(E a,E b){
	return a.val>b.val;
}
int find(int x){
	if(x==fa[x])return x;
	return fa[x]=find(fa[x]);
}
void add(int x,int y,int z){
	edge2[++tot].ver=y;
	edge2[tot].to=head[x];
	edge2[tot].w =z;
	head[x]=tot;
}
void kruskal(){
	for(int i=1;i<=n;i++)
	fa[i]=i;
	for(int i=1;i<=m;i++)
	{
	  int x=find(edge[i].x);
	  int y=find(edge[i].y);
	  if(x==y)continue;
	  fa[x]=y;
	  add(edge[i].x,edge[i].y,edge[i].val);
	  add(edge[i].y,edge[i].x,edge[i].val);
	}
}
void dfs(int x)
{
   for(int i=head[x];i;i=edge2[i].to) {
	   	int y=edge2[i].ver;
	   	if(d[y])continue;
	   	d[y]=d[x]+1;
	   	f[y][0]=x;
	   	w[y][0]=edge2[i].w;
	   	dfs(y);
   }
}
int lca(int x,int y){
	int ans=inf;
	if(find(x)!=find(y))return -1;
	if(d[x]>d[y])swap(x,y);
	for(int i=t;i>=0;i--)
		if(d[f[y][i]]>=d[x]){
		ans=min(ans,w[y][i]);
		y=f[y][i];
	}
	if(x==y)return ans;
	for(int i=t;i>=0;i--){
		if(f[x][i]!=f[y][i]){
			ans=min(ans,w[x][i]);
			ans=min(ans,w[y][i]);
			x=f[x][i];y=f[y][i];
		}
	}
	ans=min(ans,min(w[x][0],w[y][0]));
	return ans;
}
void init(){
	for(int i=1;i<=n;i++)
	{
		if(!d[i]){
			d[i]=1;
			f[i][0]=i;
			w[i][0]=inf;
			dfs(i);
		}
	}	
	for(int i=1;i<=t;i++)
	for(int j=1;j<=n;j++) 
	   	{
	    	f[j][i]=f[f[j][i-1]][i-1];
	   		w[j][i]=min(w[f[j][i-1]][i-1],w[j][i-1]);
	    }

}
int main(){
//	freopen("truck.in","r",stdin);
//	freopen("truck.out","w",stdout);
    n=read();
    m=read();
    int x,y,z;
    t=(int)(log(n)/log(2))+1;
    for(int i=1;i<=m;i++)
    {
    edge[i].x=read();
	edge[i].y=read();
	edge[i].val=read();
	}
	sort(edge+1,edge+m+1,comp);
	kruskal();
	init();
	p=read();
	for(int i=1;i<=p;i++)
	{
		x=read();
		y=read();
		cout<<lca(x,y)<<endl;
	}
	return 0;
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值