[51nod1743]雪之国度

题目大意

对于一个无向图,边有边权。对于每组询问u、v,如果可以找到两条从u到v不相交的路径,输出路径上边权最大值的最小值。

维护边双

我们考虑到,如果按照边权从小到大加边,第一次使得u和v处在同一个边双联通分量时我就求得了答案。
考虑一个很强的方法,用LCT或启发式合并维护森林,动态维护双连通分量。
因为不可能去持久化+二分求询问,我们可以再思考,每次将两个边双合并意味着一次集合合并,我们建一个新点,让原先边双对应点连向该新点,边权即为当前枚举边的边权。
只要做完一次过程,可以发现如果u到v有答案,必定是求u到v在新树(易证建出的新图是树)上树路径边权的最大值,这是个经典套路。

更简单的维护

维护森林是很麻烦的,但是注意到u和v联通才能有答案。
何不先做一次最小生成树(最小生成树也是按边权从小到大插入的!),再依次加入非树边。
这样的好处是,我们有了一颗静态树,不需要再用高级方法维护森林了!
现在我们只需要并查集和倍增就够了。

#include<cstdio>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=100000+10,maxm=500000+10;
struct dong{
    int x,y,z;
} a[maxm];
int fa[maxn+maxm],f2[maxn],w[maxn],d[maxn+maxm],zjy[maxn+maxm],f[maxn+maxm][25],g[maxn+maxm][25];
int h[maxn+maxm],from[maxm*2],go[maxm*2],nxt[maxm*2],dis[maxm*2];
bool pd[maxm];
int i,j,k,l,r,s,t,n,m,q,tot,top,root;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
bool cmp(dong a,dong b){
    return a.z<b.z;
}
int getfa(int x){
    return fa[x]?fa[x]=getfa(fa[x]):x;
}
int getfa2(int x){
    return f2[x]?f2[x]=getfa2(f2[x]):x;
}

void add(int x,int y,int z){
    go[++tot]=y;
    from[tot]=x;
    dis[tot]=z;
    nxt[tot]=h[x];
    h[x]=tot;
}
void dfs(int x,int y){
    f[x][0]=y;
    d[x]=d[y]+1;
    int t=h[x];
    while (t){
        if (go[t]!=y){
            g[go[t]][0]=dis[t];
            dfs(go[t],x);
        }
        t=nxt[t];
    }
}
void dg(int x){
    int t=h[x];
    while (t){
        d[go[t]]=d[x]+1;
        dg(go[t]);
        t=nxt[t];
    }
}
int lca(int x,int y){
    if (d[x]<d[y]) swap(x,y);
    if (d[x]!=d[y]){
        int j=zjy[d[x]];
        while (j>=0){
            if (d[f[x][j]]>=d[y]) x=f[x][j];
            j--;
        }
    }
    if (x==y) return x;
    int j=zjy[d[x]];
    while (j>=0){
        if (f[x][j]!=f[y][j]){
            x=f[x][j];
            y=f[y][j];
        }
        j--;
    }
    return f[x][0];
}
int getmx(int u,int v){
    int w=lca(u,v),t=0,j;
    j=zjy[d[u]];
    while (j>=0){
        if (d[f[u][j]]>=d[w]){
            t=max(t,g[u][j]);
            u=f[u][j];
        }
        j--;
    }
    j=zjy[d[v]];
    while (j>=0){
        if (d[f[v][j]]>=d[w]){
            t=max(t,g[v][j]);
            v=f[v][j];
        }
        j--;
    }
    return t;
}
int main(){
    freopen("city.in","r",stdin);freopen("city.out","w",stdout);
    n=read();m=read();q=read();
    fo(i,1,n) w[i]=read();
    fo(i,1,m){
        j=a[i].x=read();k=a[i].y=read();
        a[i].z=abs(w[j]-w[k]);
    }
    sort(a+1,a+m+1,cmp);
    fo(i,1,m){
        j=a[i].x;k=a[i].y;
        if (getfa(j)!=getfa(k)){
            pd[i]=1;
            add(j,k,a[i].z);add(k,j,a[i].z);
            fa[getfa(j)]=getfa(k);
        }
    }
    dfs(1,0);
    fo(i,1,n+m) zjy[i]=floor(log(n)/log(2)); 
    fo(j,1,zjy[n])
        fo(i,1,n){
            f[i][j]=f[f[i][j-1]][j-1];
            g[i][j]=max(g[i][j-1],g[f[i][j-1]][j-1]);
        }
    fo(i,1,n) fa[i]=f2[i]=0;
    top=n;
    tot=0;
    fo(i,1,n) h[i]=0;
    fo(i,1,m)
        if (!pd[i]){
            j=a[i].x;k=a[i].y;
            if (getfa(j)==getfa(k)) continue;
            ++top;
            l=getmx(j,k);
            r=lca(j,k);
            t=j;
            while (d[t]>=d[r]){
                if (d[getfa2(t)]<=d[r]) break;
                add(top,getfa(t),max(l,a[i].z));
                fa[getfa(t)]=top;
                t=f[getfa2(t)][0];
            }
            t=k;
            while (d[t]>=d[r]){
                if (d[getfa2(t)]<=d[r]) break;
                add(top,getfa(t),max(l,a[i].z));
                fa[getfa(t)]=top;
                t=f[getfa2(t)][0];
            }
            add(top,getfa(r),max(l,a[i].z));
            fa[getfa(r)]=top;
            t=j;
            while (d[t]>=d[r]){
                if (d[getfa2(t)]<=d[r]) break;
                s=getfa2(t);
                f2[getfa2(t)]=f[s][0];
                t=f[s][0];
            }
            t=k;
            while (d[t]>=d[r]){
                if (d[getfa2(t)]<=d[r]) break;
                s=getfa2(t);
                f2[getfa2(t)]=f[s][0];
                t=f[s][0];
            }
        }
    fo(i,1,top) d[i]=0;
    fo(i,1,tot)
        f[go[i]][0]=from[i],g[go[i]][0]=dis[i],d[go[i]]++;
    fo(j,1,zjy[top])
        fo(i,1,top){
            f[i][j]=f[f[i][j-1]][j-1];
            g[i][j]=max(g[i][j-1],g[f[i][j-1]][j-1]);
        }
    fo(i,1,top)
        if (!d[i]) dg(i);
    fo(i,1,q){
        j=read();k=read();
        if (getfa(j)!=getfa(k)) printf("infinitely\n");
        else printf("%d\n",getmx(j,k));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值