实际上,我们可以把并查集问题转化为维护一个数组单点修改的问题,因为:
并查集维护的是一个fa[]数组,表示x的父亲,那么我们就来维护这个fa[]数组。由于存在若干修改和历史版本,实际上我们令fa[x]表示节点x(x为在线段树中的动态添加的节点)的父亲在原数组(下表1..n)中的位置。对于3中操作:
1.合并:用类似并查集的方式找到x,y的根u,v,然后采用启发式合并(这样只需要修改一个),对每一个节点维护一个秩,每次将秩小的并到大的,相等的话根的秩+1。
2.恢复到历史版本:rt指过去就好了;
3.查询:找根比较的好了。
AC代码如下(bzoj3674的):
#include<iostream> #include<cstdio> #include<cstring> #define N 10000005 using namespace std; int n,m,trtot,rt[200005],ls[N],rs[N],fa[N],dep[N]; int read(){ int x=0; char ch=getchar(); while (ch<'0' || ch>'9') ch=getchar(); while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); } return x; } void build(int &k,int l,int r){ k=++trtot; int mid=(l+r)>>1; if (l==r){ fa[k]=l; return; } build(ls[k],l,mid); build(rs[k],mid+1,r); } void ins(int l,int r,int x,int &y,int k,int v){ y=++trtot; int mid=(l+r)>>1; if (l==r){ fa[y]=v; return; } if (k<=mid){ rs[y]=rs[x]; ins(l,mid,ls[x],ls[y],k,v); } else{ ls[y]=ls[x]; ins(mid+1,r,rs[x],rs[y],k,v); } } void updata(int l,int r,int k,int x){ if (l==r){ dep[k]++; return; } int mid=(l+r)>>1; if (x<=mid) updata(l,mid,ls[k],x); else updata(mid+1,r,rs[k],x); } int qry(int k,int l,int r,int x){ if (l==r) return k; int mid=(l+r)>>1; if (x<=mid) return qry(ls[k],l,mid,x); else return qry(rs[k],mid+1,r,x); } int getfa(int k,int x){ int t=qry(k,1,n,x); return (fa[t]==x)?t:getfa(k,fa[t]); } int main(){ n=read(); m=read(); int i,ans=0; build(rt[0],1,n); for (i=1; i<=m; i++){ int k=read(),x,y; if (k==1){ x=read()^ans; y=read()^ans; rt[i]=rt[i-1]; x=getfa(rt[i],x); y=getfa(rt[i],y); if (fa[x]!=fa[y]){ if (dep[x]>dep[y]) swap(x,y); ins(1,n,rt[i],rt[i],fa[x],fa[y]); if (dep[x]==dep[y]) updata(1,n,rt[i],fa[y]); } } else if (k==2) rt[i]=rt[read()^ans]; else{ int x=read()^ans,y=read()^ans; rt[i]=rt[i-1]; x=getfa(rt[i],x); y=getfa(rt[i],y); printf("%d\n",(fa[x]==fa[y])?ans=1:ans=0); } } return 0; }
by lych
2016.2.22