可持久化專題(三) 可持久化並查集

2021-08-22 11:37:53 字數 3367 閱讀 3822

前言

要學可持久化並查集,必須先會可持久化陣列

l in

klink

link

可持久化陣列詳見部落格可持久化專題(二)——可持久化陣列的實現

簡介

可持久化並查集應該是乙個挺實用的資料結構(例如noi2018day1t1中就有它的身影)。

它主要建立於可持久化陣列的基礎之上(而可持久化陣列的實現是完全基於主席樹的),因為這樣就可以去訪問一些歷史版本從而實現可持久化了。

l in

klink

link

主席樹詳見部落格可持久化專題(一)——**主席樹:可持久化線段樹

按秩合併

由於可持久化的緣故,我們要切記,可持久化並查集是不能像普通並查集一樣寫路徑壓縮的!

或許有人會問,不路徑壓縮,還不t

tt飛?

沒關係,沒有路徑壓縮,我們還有按秩合併,它的複雜度均攤是o(l

ogn)

o(logn)

o(logn

)的,平時由於已經有路徑壓縮了,

所以基本上沒人寫按秩合併(實在沒什麼必要,時間複雜度優化並不大),而此時此刻,沒法用路徑壓縮,按秩合併就起了很大的作用。

l in

klink

link

按秩合併的複雜度證明詳見部落格啟發式合併

具體實現

如果對可持久化並查集還有什麼不懂的,最好再多思考一下,畢竟這還是有點深奧的。

下面是洛谷上可持久化並查集板子題的**:(**比可持久化陣列長了許多)

#include

#define max(x,y) ((x)>(y)?(x):(y))

#define min(x,y) ((x)<(y)?(x):(y))

#define ll long long

#define swap(x,y) (x^y?(x^=y,y^=x,x^=y):0)

#define tc() (a==b&&(b=(a=ff)+fread(ff,1,100000,stdin),a==b)?eof:*a++)

#define pc(ch) (pp_<100000?pp[pp_++]=(ch):(fwrite(pp,1,100000,stdout),pp[(pp_=0)++]=(ch)))

#define n 200000

int pp_=0;

char ff[

100000],

*a=ff,

*b=ff,pp[

100000];

using

namespace std;

int n,q,tot=

0,rt[n+5]

,a[n+5]

;struct chairman_tree

node[n*20]

;inline

void

read

(int

&x)inline

void

write

(int x)

inline

void

build

(int

&rt,

int l,

int r)

//初始的建樹,一開始每個節點的fa都是本身,這是並查集的基礎思想

build

(node[rt]

.son[0]

,l,mid)

,build

(node[rt]

.son[1]

,mid+

1,r);}

inline

void

newpoint

(int

&rt,

int lst,

int l,

int r,

int x,

int fa)

//新插入乙個節點

//更新fa,並複製以前版本的這個節點的level

node[rt]

.son[0]

=node[lst]

.son[0]

,node[rt]

.son[1]

=node[lst]

.son[1]

;if(x<=mid)

newpoint

(node[rt]

.son[0]

,node[lst]

.son[0]

,l,mid,x,fa)

;else

newpoint

(node[rt]

.son[1]

,node[lst]

.son[1]

,mid+

1,r,x,fa);}

inline

void

add_level

(int rt,

int l,

int r,

int x)

//增加乙個節點的在按秩合併時的優先順序

if(x<=mid)

add_level

(node[rt]

.son[0]

,l,mid,x)

;else

add_level

(node[rt]

.son[1]

,mid+

1,r,x);}

inline

intquery

(int rt,

int l,

int r,

int x)

//詢問x節點在某一版本下的位置

inline

intgetfa

(int rt,

int x)

//詢問x節點在某一版本下的祖先

inline

void

connect

(int v,

int x,

int y)

//在版本v中連線x和y,將他們放入乙個集合中

intmain()

}return

fwrite

(pp,

1,pp_,

stdout),

0;}

可持久化陣列與可持久化並查集

可持久化陣列 維護這樣的乙個長度為 n 的陣列,支援如下幾種操作 在某個歷史版本上修改某乙個位置上的值 訪問某個歷史版本上的某一位置的值 此外,每進行一次操作 對於操作2,即為生成乙個完全一樣的版本,不作任何改動 就會生成乙個新的版本。版本編號即為當前操作的編號 從1開始編號,版本0表示初始狀態陣列...

可持久化並查集

n個集合 m個操作 1 a b 合併a,b所在集合 2 k 回到第k次操作之後的狀態 查詢算作操作 3 a b 詢問a,b是否屬於同一集合,是則輸出1否則輸出0 所給的a,b,k均經過加密,加密方法為x x xor lastans,lastans是上一次的輸出答案 並查集實質是乙個陣列,可持久化並查...

可持久化並查集

可持久化陣列 可持久化陣列是一種可以回退,訪問之前版本的陣列 是一些其他可持久化資料結構的基石 例如可持久化並查集 與普通並查集不同的是 這裡用到了 按秩合併新增鏈結描述 include const int n 2e5 7 int rootfa n rootdep n cnt,tot struct ...