【题解】可持久化并查集
\[\mathcal{Solution:}
\]
首先考虑一个常用的并查集是如何实现的。显然为了方便实现,我们大部分时间用的都是路径压缩。
但是当需要可持久化的时候,如果我们进行了路径压缩,那么我们就会发现,我们会破坏原有的并查集结构,也就破坏了可持久化的结构基础。
所以我们考虑另一种维护方式:按秩合并。这里我们选择按照深度合并。
我们观察一般并查集是如何按秩合并的。我们会将两棵树比较深度 将深度小的一个合并到深度大的树的根上。这样我们才保证了深度的平衡。
注意到什么时候深度会增加:当且仅当合并的两棵树深度相同。
那么到这里并查集就梳理的差不多了 考虑如何可持久化。
像可持久化数组一样 这里我们可以考虑用可持久化线段树来维护并查集的 \(fa\) 数组 (代码里面是 f[]
)
那先考虑如何查找父亲 我们可以直接在对应版本的线段树上进行单点查询 主席树上每个点维护这个点在这个版本的父亲。
由启发式合并(按秩合并)容易知道 这样一直走就是 \(\log\) 的
那么我们就可以不断在主席树上进行单点查询来找到其树根。这一过程是 \(O(\log ^2 n )\) 的
那么如何合并?我们先找到两个点所在集合的根 如果相同自然就不需要合并 直接舍弃。
否则 我们考虑查询出对应点的深度 将小的往大的里面合并。具体来说的话 其实就是强制让深度小的树的根的父亲是深度大的树的根 这一步骤只需要一个单点修改 但是注意 这里需要新建版本
所以这一步我们进去查询就直接一边建立点一边继承信息修改即可。一定注意 继承信息
然后再判断一下深度是不是一样 如果一样的话就把当根的那个点的深度 \(+1\) 即可
到这里合并就做完了
那么撤销怎么做?
简单 直接找到对应版本号即可
那么最后考虑询问 由于我们知道了版本号 直接在上面 find()
即可
复杂度大概就是两个 $\log $ 了。
#include <bits/stdc++.h>
using namespace std;
typedef double db;
//#define int long long
#define fi first
#define se second
#define mk make_pair
#define pb emplace_back
#define poly vector<int>
#define Bt(a) bitset<a>
#define bc __builtin_popcount
#define pc putchar
#define ci const int&
const int mod = 1e9 + 7;
const db eps = 1e-10;
inline int Max(ci x, ci y) {return x > y ? x : y;}
inline int Min(ci x, ci y) {return x < y ? x : y;}
inline db Max(db x, db y) {return x - y > eps ? x : y;}
inline db Min(db x, db y) {return x - y < eps ? x : y;}
inline int Add(ci x, ci y, ci M = mod) {return (x + y) % M;}
inline int Mul(ci x, ci y, ci M = mod) {return 1ll * x * y % M;}
inline int Dec(ci x, ci y, ci M = mod) {return (x - y + M) % M;}
typedef pair<int, int> pii;
inline int Abs(int x) {return x < 0 ? -x : x;}
//char buf[1<<21],*p1=buf,*p2=buf;
//#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char Obuf[105000],*O=Obuf;//Siz shoule be the size of Out File
int pst[30],ptop;
inline void Fprint(){fwrite(Obuf,1,O-Obuf,stdout);}
inline void Fwrite(int x){
if(x<0)*O++='-',x=-x;ptop=0;
while(x)pst[++ptop]=x%10,x/=10;
while(ptop)*O++=pst[ptop--]+'0';
if(O-Obuf>100000)Fprint(),O=Obuf;
}
inline int read() {
int s = 0, w = 1;
char ch = getchar();
while (!isdigit(ch)) {if (ch == '-') w = -1;ch = getchar();}
while (isdigit(ch)) {s = s * 10 + ch - '0';ch = getchar();}
return s * w;
}
inline void write(int x) {
if (x < 0)putchar('-'), x = -x;
if (x > 9)write(x / 10);
pc(x % 10 + '0');
}
inline int qpow(int x, int y) {
int res = 1;
while (y) {if (y & 1)res = Mul(res, x);x = Mul(x, x);y >>= 1;}
return res;
}
inline void cadd(int &x, int y) {x += y;}
inline void cmul(int &x, int y) {x *= y;}
inline void cmax(int &x, int y) {x = Max(x, y);}
inline void cmin(int &x, int y) {x = Min(x, y);}
const int N = 2e5 + 10;
namespace Refined_heart{
const int SN=64000000;
int ls[SN],rs[SN],f[SN],dep[SN],node;
int n,m,rt[N];
void build(int &x,int l,int r){
x=++node;
if(l==r){
f[x]=l;
return;
}
int mid=(l+r)>>1;
build(ls[x],l,mid);
build(rs[x],mid+1,r);
}
int change(int pre,int l,int r,int pos,int F){
int p=++node;
ls[p]=ls[pre];
rs[p]=rs[pre];
f[p]=f[pre];
dep[p]=dep[pre];
if(l==r){
f[p]=F;
dep[p]=dep[pre];
return p;
}
int mid=(l+r)>>1;
if(pos<=mid)ls[p]=change(ls[pre],l,mid,pos,F);
else rs[p]=change(rs[pre],mid+1,r,pos,F);
return p;
}
void change_point(int pre,int l,int r,int pos){
if(l==r){
++dep[pre];
return;
}
int mid=(l+r)>>1;
if(pos<=mid)change_point(ls[pre],l,mid,pos);
else change_point(rs[pre],mid+1,r,pos);
}
int query(int x,int l,int r,int pos){
if(l==r)return x;
int mid=(l+r)>>1;
if(pos<=mid)return query(ls[x],l,mid,pos);
return query(rs[x],mid+1,r,pos);
}
int find(int id,int x){
int F=query(id,1,n,x);
if(x==f[F])return F;
return find(id,f[F]);
}
void solve(){
n=read();m=read();
build(rt[0],1,n);
for(int i=1;i<=m;++i){
int op=read();
if(op==1){
rt[i]=rt[i-1];
int a=read(),b=read();
int fa=find(rt[i],a);
int fb=find(rt[i],b);
if(f[fa]==f[fb])continue;
if(dep[fa]>dep[fb])swap(fa,fb);
rt[i]=change(rt[i-1],1,n,f[fa],f[fb]);
if(dep[fa]==dep[fb])change_point(rt[i],1,n,f[fb]);
continue;
}
if(op==2){
int k=read();
rt[i]=rt[k];
continue;
}
if(op==3){
rt[i]=rt[i-1];
int a=read(),b=read();
int fa=find(rt[i],a);
int fb=find(rt[i],b);
if(f[fa]==f[fb])pc('1'),pc('\n');
else pc('0'),pc('\n');
}
}
}
}
int main(){
Refined_heart::solve();
return 0;
}