CF938G Shortest Path Queries

题目描述

题解

考虑到删边或者改边权不好操作,于是我们可以线段树分治,这样就只有加边并且边权确定。

考虑一下如何求一张图上一个点到另一个点的最小异或值,那这是一个经典问题,考虑到图上的环的异或值都可以取出,并且三个有交集的环两个异或后就是第三个,所以我们如果对于一棵树加上一条非树边,那就直接把这个环的异或值扔到线性基里即可。维护树的形态可以用可持久化并查集,即启发式合并即可。

效率: $O(nlog^2n)$ 。

代码

#include <bits/stdc++.h>
#define M make_pair
using namespace std;
const int N=4e5+5;
int n,m,t,c,U[N],Q,V[N],f[N],s[N],a[20][30],p[N];
map<pair<int,int>,int>mp;
struct O{int x,y,z;}e[N],q[N];
vector<O>g[N<<1],b[N<<1];
void ins(int x,int y,int z){
    e[++c]=(O){x,y,z};
    U[c]=t+1;V[c]=-1;mp[M(x,y)]=c;
}
int F(int x){while(x!=f[x]) x=f[x];return x;}
int G(int x){int v=0;while(x!=f[x]) v^=p[x],x=f[x];return v;}
#define Ls k<<1
#define Rs k<<1|1
#define mid ((l+r)>>1)
void upd(int k,int l,int r,int L,int R,int i){
    if (L<=l && r<=R){
        g[k].push_back(e[i]);return;
    }
    if (mid>=L) upd(Ls,l,mid,L,R,i);
    if (mid<R) upd(Rs,mid+1,r,L,R,i);
}
void qry(int k,int l,int r,int d){
    for (int i=0;i<30;i++) a[d][i]=a[d-1][i];
    int z=g[k].size(),w;
    for (int x,y,u,v,i=0;i<z;i++){
        u=g[k][i].x;v=g[k][i].y;
        w=g[k][i].z;x=F(u);y=F(v);w^=G(u)^G(v);
        if (x==y){
            for (int i=29;~i;i--)
                if (w&(1<<i)){
                    if (!a[d][i]){
                        a[d][i]=w;break;
                    }
                    w^=a[d][i];
                }
            continue;
        }
        if (s[x]>s[y]) swap(x,y),swap(u,v);
        b[k].push_back((O){x,y,s[y]});
        p[x]=w;f[x]=y;s[y]+=s[x];
    }
    if (l==r){
        w=G(q[l].x)^G(q[l].y);
        for (int i=29;~i;i--)
            w=min(w,w^a[d][i]);
        printf("%d
",w);
    }
    else qry(Ls,l,mid,d+1),qry(Rs,mid+1,r,d+1);
    z=b[k].size();
    for (int u,i=z-1;~i;i--)
        s[b[k][i].y]=b[k][i].z,
        p[u=b[k][i].x]=0,f[u]=u;
}
int main(){
    cin>>n>>m;
    for (int x,y,z,i=1;i<=m;i++){
        scanf("%d%d%d",&x,&y,&z);
        if (x>y) swap(x,y);ins(x,y,z);
    }
    cin>>Q;
    for (int op,x,y,z;Q--;){
        scanf("%d%d%d",&op,&x,&y);
        if (x>y) swap(x,y);
        if (op<2) scanf("%d",&z),
            V[mp[M(x,y)]]=t,ins(x,y,z);
        else if (op<3)
            V[mp[M(x,y)]]=t,mp[M(x,y)]=0;
        else q[++t]=(O){x,y,0};
    }
    for (int i=1;i<=c;i++){
        if (!~V[i]) V[i]=t;
        if (U[i]<=V[i]) upd(1,1,t,U[i],V[i],i);
    }
    for (int i=1;i<=n;i++) f[i]=i,s[i]=1;
    qry(1,1,t,1);return 0;
}
原文地址:https://www.cnblogs.com/xjqxjq/p/12451722.html