P1967 [NOIP2013 提高组] 货车运输

[NOIP2013 提高组] 货车运输 - 洛谷

核心思路

建最大生成树,树上维护倍增祖先,倍增最小值

#include<bits/stdc++.h>
#define INF 1e9+7
using namespace std;
const int N = 10005;
int n,m;
int val[N];
int f[N],deep[N];
bool vis[N];
int fa[N][22],w[N][22];
struct edge{
	int u,v,w;
}e[N];
vector<edge> g[N];
void dfs(int u){
	vis[u] = 1;
	for(edge vv:g[u]){
		int v = vv.v;
		if(vis[v]) continue;
		fa[v][0] = u;
		w[v][0] = vv.w; 
		//cout<<u<<" "<<v<<" "<<vv.w<<endl;
		deep[v] = deep[u]+1;
		dfs(v);
	}
}
bool cmp(edge a,edge b){
	return a.w > b.w;
}
int find(int x){  //并查集寻找父节点 
    if(f[x]!=x) f[x]=find(f[x]);
    return f[x];
}
void hb(int u,int v){
	f[find(u)] = find(v);
}
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].u)!=find(e[i].v)){
            hb(e[i].u,e[i].v);
            g[e[i].u].push_back({0,e[i].v,e[i].w});
            g[e[i].v].push_back({0,e[i].u,e[i].w}); //无向图,双向加边 
        }
    }
    return ;
}
int lca(int x, int y)
{
    if(find(x)!=find(y)) return -1; //不连通,输出-1 
    int ans=INF;
    if(deep[x]>deep[y]) swap(x,y); 
 
    for(int i=20; i>=0; i--)
        if(deep[fa[y][i]]>=deep[x]){
        	//out<<w[y][i]<<endl;
            ans=min(ans,w[y][i]);  
            y=fa[y][i];  
        }
    if(x==y) return ans;  
    for(int i=20; i>=0; i--)
        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]));

    return ans;
}
int main(){
	cin>>n>>m;
	for(int i = 1;i <= m;i++){
		int u,v,w;
		cin>>u>>v>>w;
		e[i].u = u,e[i].v = v,e[i].w = w;	
	}
	kruskal();
	for(int i=1; i<=n; i++)
        if(!vis[i]){ //dfs收集信息 
            deep[i]=1; 
            dfs(i);
            fa[i][0]=i;
            w[i][0]=INF;
        }
    for(int i=1; i<=20; i++)
        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]);
        }
    int q;
    cin>>q;
    while(q--){
    	int x,y;
    	cin>>x>>y;
    	cout<<lca(x,y)<<endl;
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值