题意:中文
题目链接:AcWing270. 可持久化并查集加强版
前置知识:可持久化数组,主席树。
每个版本用一个线段树来维护节点的父亲是谁,该联通块的节点数量,线段树只有叶子节点有意义,其余都是无意义的。
最开始的时候,所有节点的父亲都是自己,每个联通块的秩都为1。
按主席树那样用root来表示第k个版本对应的线段树。
对于合并操作,按秩合并,将秩较小的合并到大的上面,相当于两次更新,第一次将x的父亲改为y,第二次将y的节点数量更改。
对于版本退化,直接将root[i]=root[k]即可。
查询就更好做了。
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+7;
struct Node{
int fa,siz,lc,rc;
}X[maxn*35];
int root[maxn],tot;
int build(int l,int r){
int k=++tot;
if(l==r){
X[k].fa=l,X[k].siz=1;
return k;
}
int mid=l+r>>1;
X[k].lc=build(l,mid);
X[k].rc=build(mid+1,r);
return k;
}
int upd(int p,int l,int r,int id,int fa,int siz){
int k=++tot;
X[k]=X[p];
if(l==r){
X[k].fa=fa,X[k].siz=siz;
return k;
}
int mid=l+r>>1;
if(id<=mid) X[k].lc=upd(X[p].lc,l,mid,id,fa,siz);
else X[k].rc=upd(X[p].rc,mid+1,r,id,fa,siz);
return k;
}
Node ask(int p,int l,int r,int id){
if(l==r) return X[p];
int mid=l+r>>1;
if(id<=mid) return ask(X[p].lc,l,mid,id);
return ask(X[p].rc,mid+1,r,id);
}
int n;
//root[p];
Node myfind(int p,int x){
Node f=ask(root[p],1,n,x);
if(f.fa==x) return f;
return myfind(p,f.fa);
}
//root[p]; p is last;
void marge(int p,int x,int y){
Node fx=myfind(p,x),fy=myfind(p,y);
if(fx.fa==fy.fa) return ;
if(fx.siz>fy.siz) swap(fx,fy);
int t=upd(root[p],1,n,fx.fa,fy.fa,fx.siz);
root[p+1]=upd(t,1,n,fy.fa,fy.fa,fx.siz+fy.siz);
}
int main(){
int q,id,x,y,res=0;
scanf("%d%d",&n,&q);
root[0]=build(1,n);
for(int i=1;i<=q;++i){
root[i]=root[i-1];
scanf("%d",&id);
if(id==1){ scanf("%d%d",&x,&y); x^=res,y^=res; marge(i-1,x,y); }
else if(id==2){ scanf("%d",&x); x^=res; root[i]=root[x]; }
else{ scanf("%d%d",&x,&y); x^=res,y^=res; res=(myfind(i,x).fa==myfind(i,y).fa?1:0); printf("%d\n",res); }
}
return 0;
}