题解
考虑到删边或者改边权不好操作,于是我们可以线段树分治,这样就只有加边并且边权确定。
考虑一下如何求一张图上一个点到另一个点的最小异或值,那这是一个经典问题,考虑到图上的环的异或值都可以取出,并且三个有交集的环两个异或后就是第三个,所以我们如果对于一棵树加上一条非树边,那就直接把这个环的异或值扔到线性基里即可。维护树的形态可以用可持久化并查集,即启发式合并即可。
效率: $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; }