【題解】可持久化並查集

可持久化並查集

\[\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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章