货车运输 NOIP 2013 day1 t3

本文介绍了一种解决图论问题的方法,利用最大生成树、倍增法和LCA算法来寻找两个节点间路径上的最小边权的最大值。首先通过Kruskal算法构造最大生成树,接着使用倍增法进行预处理,最后通过LCA算法确定路径。
摘要由CSDN通过智能技术生成

这个题目想A掉两个多月了,今天终于AC了,好开心 _

题意是 有一个有n个点m条边的图,每次询问两个点路径上最小边权的最大值。

根据最大生成树的性质,先kruskal求出最大生成树,有g存储新的生成树。

然后倍增相当于在树上求出两两点的路径上最小边权的最大值。

之后通过dfs解决 f [ i ] [ 0 ] , w [ i ] [ 0 ] , d e p [ i ] f[i][0],w[i][0],dep[i] f[i][0],w[i][0],dep[i]

这个可以用 L C A LCA LCA解决。

f [ i ] [ j ] f[i][j] f[i][j]表示i的第 2 j 2^j 2j个父亲
w [ i ] [ j ] w[i][j] w[i][j]表示i到第 2 j 2^j 2j个父亲这一段区间中最小边权的值。

调用的时候

f [ i ] [ j ] = f [ f [ i ] [ j − 1 ] ] [ j − 1 ] f[i][j]=f[f[i][j-1]][j-1] f[i][j]=f[f[i][j1]][j1] i i i的第 2 j 2^j 2j个父亲=i的第 2 j − 1 2^{j-1} 2j1个父亲的 2 j − 1 2^{j-1} 2j1个父亲
w [ i ] [ j ] = m i n ( w [ i ] [ j − 1 ] , w [ f [ i ] [ j − 1 ] ] [ j − 1 ] ) w[i][j]=min(w[i][j-1],w[f[i][j-1]][j-1]) w[i][j]=min(w[i][j1],w[f[i][j1]][j1]) 同理

这个枚举必须先j再i,不然WA10.

之后都是常规的 L C A LCA LCA

先保证x的深度< y的深度,

然后跳y,跳到与x同样深度,之后x,y一起跳,如果 f [ x ] [ i ] = f [ y ] [ i ] f[x][i]=f[y][i] f[x][i]=f[y][i]说明可能跳过了,要缩小i。直到 f [ x ] [ 0 ] = f [ y ] [ 0 ] f[x][0]=f[y][0] f[x][0]=f[y][0]

#include <bits/stdc++.h>
using namespace std ;
const int N = 10010 ;
const int M = 50010 ;
const int inf = 999999999 ;
struct edge{
    int from,to,w ;
}e[M];
struct node{
    int to,next,w ;
}g[M<<1];
int dad[N],head[N],dep[N],w[N][25],f[N][25],vis[N] ;
int n,m,Q,tot ;
int find(int x){
    return (dad[x]==x)?x:dad[x]=find(dad[x]) ;
} 
bool cmp(edge a,edge b){
    return a.w>b.w ;
}
void add(int x,int y,int w){
    g[++tot].to=y ;
    g[tot].w=w ;
    g[tot].next=head[x] ;
    head[x]=tot ;
}
void kruskal(){
    for (int i=1;i<=n;i++) dad[i]=i ;
    sort(e+1,e+m+1,cmp) ;
    for (int i=1;i<=m;i++){
        int X=find(e[i].from),Y=find(e[i].to) ;
        if (X!=Y){
            dad[X]=Y ;
            add(e[i].from,e[i].to,e[i].w) ;
            add(e[i].to,e[i].from,e[i].w) ;
        }
    }
}
void dfs(int rt){ //预处理 
    vis[rt]=true;
    for (int i=head[rt];i;i=g[i].next){
        int to=g[i].to;
        if (vis[to]) continue ;
        dep[to]=dep[rt]+1 ;
        w[to][0]=g[i].w ;
        f[to][0]=rt ;
        dfs(to) ;
    }
}
int lca(int x,int y){
    if (find(x)!=find(y)) return -1 ;
    int ans=inf ;
    if (dep[x]>dep[y]) swap(x,y) ;//保证x的深度<y的深度,跳y
    for (int i=20;i>=0;i--){
        if (dep[x]<=dep[f[y][i]]){
            ans=min(ans,w[y][i]) ;
            y=f[y][i] ;
        }
    }
    if (x==y) return ans ;
    for (int i=20;i>=0;i--){
        if (f[x][i]!=f[y][i]){
            ans=min(ans,min(w[x][i],w[y][i])) ;
            x=f[x][i] ;
            y=f[y][i] ;
        }
    }
    ans=min(ans,min(w[x][0],w[y][0])) ;
    return ans ;
}
int main(){
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++) scanf("%d%d%d",&e[i].from,&e[i].to,&e[i].w) ;
    kruskal() ;
    for (int i=1;i<=n;i++){
        if (!vis[i]){
            dep[i]=1 ;
            dfs(i);
            w[i][0]=inf ;
            f[i][0]=i ; 
        } 
    }
    for (int j=1;j<=20;j++){
        for (int i=1;i<=n;i++){
            f[i][j]=f[f[i][j-1]][j-1] ;
            w[i][j]=min(w[i][j-1],w[f[i][j-1]][j-1]) ;
        }	
    }
    scanf("%d",&Q) ;
    for (int i=1;i<=Q;i++){
        int a,b ;
        scanf("%d%d",&a,&b) ;
        printf("%d\n",lca(a,b)) ;
    }
}
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值