[51nod1743]雪之国度

Description

给出一张n个点m条边的无向联通图,点i的点权为w[i],边(x,y)的边权为|w[x]-w[y]|
q次询问,每次询问一个点对(x,y)是否存在两条不相交(边相交)的路径,如果存在,输出这两条路径上的边权最大值(如果有多中方案选最小的)
3<=N<=100000, 3<=M<=500000, 1<=Q<=100000

Solution

额,这种东西一般都是维护边双啦。。。
我们把边权从小到大排序,然后动态加点维护,x,y两个点的答案就是他们第一次存在于同一个边双时的边权。。。
然后这东西需要维护森林?!LCT启发式合并点双?!
什么鬼,不会写了(估计也口胡错了)

维护森林很麻烦,我们想点其他办法。
注意(x,y)需要维护联通性,在已经联通的情况下再次联通(大概是这个意思吧,感性理解一下)
那么我们怎样才能这么维护呢?
先做一遍mst嘛。。。然后其余的非树边加边的时候把这条边所连接的两个点在树上的路径的边权全部修改,注意修改过一次就不用再修改了,因为答案显然不会更优。
那么我们只需要用并查集来维护这个东西,答案用倍增求最大值就好了

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define rep(i,a) for(int i=last[a];i;i=next[i])
using namespace std;
const int N=1e5+5;
int n,m,q,l,x,y,tot,w[N],d[N],fa[N],f[N][17],g[N][17];
int last[N],next[N*2],t[N*2],v[N*2];
bool bz[N*5];
struct note{int x,y,z;}a[N*5];
bool cmp(note x,note y) {return x.z<y.z;}
int get(int x) {
    return fa[x]?fa[x]=get(fa[x]):x;
}
void add(int x,int y,int z) {
    t[++l]=y;v[l]=z;next[l]=last[x];last[x]=l;
}
void dfs(int x,int y) {
    d[x]=d[y]+1;f[x][0]=y;g[x][0]=z;
    fo(j,1,16) f[x][j]=f[f[x][j-1]][j-1];
    rep(i,x) if (t[i]!=y) dfs(t[i],x,v[i]);
}
void jump(int &x,int z) {
    g[x][0]=z;fa[x]=get(f[x][0]);x=fa[x];
}
int lca(int x,int y) {
    if (d[x]<d[y]) swap(x,y);int ans=0;
    fd(j,16,0) if (d[f[x][j]]>d[y]) 
    ans=max(ans,g[x][j]),x=f[x][j];
    if (d[x]!=d[y]) ans=max(ans,g[x][0]),x=f[x][0];
    fd(j,16,0) if (f[x][j]!=f[y][j]) 
    ans=max(ans,max(g[x][j],g[y][j])),x=f[x][j],y=f[y][j];
    if (x!=y) return max(ans,max(g[x][0],g[y][0]));
    else return ans;
}
int main() {
    scanf("%d%d%d",&n,&m,&q);
    fo(i,1,n) scanf("%d",&w[i]);
    fo(i,1,m) scanf("%d%d",&a[i].x,&a[i].y),a[i].z=abs(w[a[i].x]-w[a[i].y]);
    sort(a+1,a+m+1,cmp);
    fo(i,1,m) {
        int x=get(a[i].x),y=get(a[i].y);
        if (x!=y) {
            add(a[i].x,a[i].y,a[i].z);
            add(a[i].y,a[i].x,a[i].z);
            fa[y]=x;tot++;bz[i]=1;
        }
        if (tot==n-1) break;
    }
    dfs(1,0,0);memset(fa,0,sizeof(fa));
    fo(i,1,m) if (!bz[i]) {
        int x=get(a[i].x),y=get(a[i].y);
        while (x!=y) {
            if (d[x]<d[y]) jump(y,a[i].z);
            else jump(x,a[i].z);
        }
    }
    fo(j,1,16) fo(i,1,n) g[i][j]=max(g[f[i][j-1]][j-1],g[i][j-1]);
    for(;q;q--) {
        scanf("%d%d",&x,&y);
        if (get(x)!=get(y)) {printf("infinitely\n");continue;}
        printf("%d\n",lca(x,y));
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值