帶撤銷並查集 可持久化並查集

2022-05-11 04:55:01 字數 3324 閱讀 7268

帶撤銷並查集支援從某個元素從原來的集合中撤出來,然後加入到乙個另外乙個集合中,或者刪除該元素

用乙個對映來表示元素和並查集中序號的關係,**中用\(to[x]\) 表示x號元素在並查集中的 id

刪除 x 號元素時,需要將 \(to[x]\) 的集合大小減去1,然後令 \(to[x]=-1\) 標記 x 刪除即可

如果要重新加入乙個元素,那麼給x分配乙個新的 id,\(to[x] = newid\)

例題1:

#include using namespace std;

const int n = 4000010;

int fa[n],to[n],sz[n],cnt,a[n],b[n],tot;

int n,m,k,x,y;

int ok[n];

int find(int x)

void merge(int x,int y)

// 將x從原集合刪除,加入到 y 所屬的集合

void update(int x,int y)

int main()

if(k == 1)merge(x,y);

if(k == 2)update(x,y);

if(k == 4)

if(k == 3)printf("%d\n",sz[find(to[x])]-1);

}for(int i=1;i<=tot;i++)

int res = -1;

for(int i=1;i<=n;i++)

if(!ok[find(to[i])])res = max(res,sz[find(to[i])]);

cout《例題2:

#includeusing namespace std;

typedef long long ll;

const int inf = 0x3f3f3f3f;

#define dbg(x...) do while (0)

void err()

templatevoid err(const t& arg,const ts&... args)

const int n = 1000000 + 50;

struct nodeo[n];

vectorg[n];

int n, m;

int fa[n*2], dep[n*2], sz[n*2], to[n], tot;

int res[n];

int find(int x)

/*1 k a b merge(a, b)

2 k a del(a)

3 k a b merge(a,b)

4 k a b find(a) == find(b)

5 k a sz[a]

*/void dfs(int u)

int rx = find(to[x]), ry = find(to[y]);

if(rx == ry)

else if(dep[rx] == dep[ry]) else

} else if(op == 2)

int rx = find(to[x]);

int t = to[x];

to[x] = -1;

sz[rx] --;

dfs(id);

to[x] = t;

sz[rx] ++;

} else if(op == 3)

int t = to[x];

int rx = find(to[x]), ry = find(to[y]);

sz[rx]--;

to[x] = ++tot;

sz[to[x]] = 1;

fa[to[x]] = ry;

sz[ry] ++;

dfs(id);

sz[ry] --;

sz[rx] ++;

to[x] = t;

} else else

} else if(op == 5)

}dfs(id);}}

}int main();

}for(int i=1;i<=n;i++) to[i] = i, fa[i] = i, sz[i] = 1;

tot = n;

dfs(0);

for(int i=1;i<=m;i++)

}return 0;

}

可持久化並查集支援查詢任一歷史版本的資訊。並查集資訊用陣列 \(fa\) 表示,合併集合時,基本操作有兩種,乙個是路徑壓縮(可能會修改很多個fa),乙個是按秩合併(啟發式合併思路,小的集合向大的合併,只會修改一次fa),由於要儲存歷史資訊,按照可持久化的一貫思路,每一次操作都會新開 logn 的空間,所以這裡要用按秩合併。

注意按秩合併的find函式寫法也有所不同,不能修改fa

此時問題就是維護每個版本的fa陣列

模版題:

const int n = 200000 + 5;

int n, m;

struct treenodet[n*30];

int root[n], tot;

int fa[n*30], dep[n*30];

void build(int &p, int l, int r)

int mid = l + r >> 1;

build(t[p].l, l, mid);

build(t[p].r, mid+1, r);

}// 修改p版本的pos位置的fa值

void change(int last, int &p, int l, int r, int pos, int val)

int mid = l + r >> 1;

if(pos <= mid)

change(t[last].l, t[p].l, l, mid, pos, val);

else

change(t[last].r, t[p].r, mid+1, r, pos, val);

} int getindex(int p, int l, int r,int pos)

int find(int p, int x)

void merge(int last, int &p, int x, int y)

}int main()

else if(op == 2) root[i] = root[x];

else

}return 0;

}

例題:

先跑一次最短路,然後按照邊權從大到小用帶權並查集維護集合到源點的最短距離。保留所有版本的資訊,然後對於每次查詢找到對應版本號回答問題即可

複雜度:\(o(n\log m + (m+q) \log^2n)\)

可持久化並查集 可撤銷並查集

主要學習的blog 1 將乙個點的父親 並查集那個fa 進行更改 實際上是新增乙個資訊點 update。2 查詢某個時間點下乙個pos對應的資訊點編號。3 查詢乙個點的父節點。4 更新乙個點的deep值,不用新寫函式,可以用3號操作找到編號後 5 初始化build。pragma gcc optimi...

演算法筆記 可撤銷並查集 可持久化並查集

可撤銷並查集模板 struct ufs inline int find int x inline void merge int x,int y fa x y if rnk x rnk y rnk y else fa y x inline void undo ufs 可持久化並查集模板 struct ...

可持久化並查集

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是上一次的輸出答案 並查集實質是乙個陣列,可持久化並查...