实际上,我们可以把并查集问题转化为维护一个数组单点修改的问题,因为:
并查集维护的是一个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