昨日做题时,见久不作过可持久化并查集矣,将忘光矣,乃有此博客。
可持久化并查集和普通并查集差不多,只是多了一个回退的操作。
一般支持的操作为:
-
a b 合并a,b所在集合
-
k 回到第k次操作之后的状态(查询算作操作)
-
a b 询问a,b是否属于同一集合,是则输出1否则输出0
怎么实现回退操作呢?我们就把普通的 f[] 数组变为可持久化的数组即可。但是我们不能路径压缩(会破坏原本树的结构),所以我们还要维护树的高度,来按秩合并。
例题:可持久化并查集例题
AC代码:
#pragma GCC optimize(2)
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=2e5+10;
int rt1[N],rt2[N],n,m,cnt;
struct node{int l,r,s;}t[N*40];
void build(int l,int r,int &x){
x=++cnt;
if(l==r){t[x].s=l; return ;}
int mid=l+r>>1;
build(l,mid,t[x].l); build(mid+1,r,t[x].r);
}
void change(int l,int r,int &x,int y,int pos,int val){
x=++cnt; t[x]=t[y];
if(l==r){t[x].s=val; return ;}
int mid=l+r>>1;
if(pos<=mid) change(l,mid,t[x].l,t[y].l,pos,val);
else change(mid+1,r,t[x].r,t[y].r,pos,val);
}
int ask(int l,int r,int x,int pos){
if(l==r) return t[x].s;
int mid=l+r>>1;
if(pos<=mid) return ask(l,mid,t[x].l,pos);
else return ask(mid+1,r,t[x].r,pos);
}
int find(int x,int k){
int fa=ask(1,n,rt1[k],x);
return x==fa?x:find(fa,k);
}
void merge(int x,int y,int k){
x=find(x,k-1),y=find(y,k-1);
if(x==y){rt1[k]=rt1[k-1],rt2[k]=rt2[k-1]; return ;}
int hx=ask(1,n,rt2[k-1],x),hy=ask(1,n,rt2[k-1],y);
if(hx>hy){change(1,n,rt1[k],rt1[k-1],y,x); rt2[k]=rt2[k-1];}
else if(hy>hx){change(1,n,rt1[k],rt1[k-1],x,y); rt2[k]=rt2[k-1];}
else if(hx==hy){
change(1,n,rt1[k],rt1[k-1],x,y);
change(1,n,rt2[k],rt2[k-1],y,hx+1);
}
}
signed main(){
cin>>n>>m; build(1,n,rt1[0]);
for(int i=1,op,a,b;i<=m;i++){
scanf("%d %d",&op,&a);
if(op==1) scanf("%d",&b),merge(a,b,i);
else if(op==2) rt1[i]=rt1[a],rt2[i]=rt2[a];
else{
rt1[i]=rt1[i-1],rt2[i]=rt2[i-1];
scanf("%d",&b),printf("%d\n",find(a,i)==find(b,i));
}
}
return 0;
}