貌似帶修資料結構都可持久 (求你們不要槓, 我只是起個標題): 可持久化並查集
阿新 • • 發佈:2021-07-07
可持久化並查集
可持久化, 隨機訪問一個數據結構在經歷 \(k\) 次操作後的結果.
並查集, 維護元素對集合從屬關係的資料結構.
可持久化並查集, 支援如下操作的資料結構:
-
合併兩個元素所在集合
-
全域性回到某時刻的狀態
-
查詢兩個元素是否在同一集合
首先, 我們先考慮並查集需要維護什麼.
\(Fa_i\), 表示一個元素的父親節點, 也就是 \(i\) 屬於 \(Fa_i\) 所屬的集合.
每次查詢或合併時, 有一定機率對 \(Fa\) 陣列進行單點修改和單點查詢.
所以我們只要將這個陣列持久化即可, 而可持久化陣列是我們已經掌握了的.
這個題應該是我做過最水的板子紫題了吧.
實現
unsigned a[10005], m, n, Cnt(0), A, B, Pos, ActVal, t, Ans(0), Tmp(0); char b[10005]; struct Node { Node *LS, *RS; unsigned Val; }N[4000005], *Ver[200005], *CntN(N); Node *Build(unsigned L, unsigned R) { register Node *x(++CntN); if(L ^ R) { register unsigned Mid((L + R) >> 1); x->LS = Build(L, Mid); x->RS = Build(Mid + 1, R); } else { x->Val = ++Cnt; } return x; } void Chg(Node *x, Node *y, unsigned L, unsigned R) { if(L ^ R) { register unsigned Mid((L + R) >> 1); if(Pos <= Mid) { if(y->LS == x->LS) y->LS = ++CntN, y->LS->LS = x->LS->LS, y->LS->RS = x->LS->RS; Chg(x->LS, y->LS, L, Mid); } else { if(y->RS == x->RS) y->RS = ++CntN, y->RS->LS = x->RS->LS, y->RS->RS = x->RS->RS; Chg(x->RS, y->RS, Mid + 1, R); } } else { y->Val = ActVal; } } void Qry(Node *x, unsigned L, unsigned R) { if(L ^ R) { register unsigned Mid((L + R) >> 1); if(Pos <= Mid) { Qry(x->LS, L, Mid); } else { Qry(x->RS, Mid + 1, R); } } else { ActVal = x->Val; } } unsigned Find(unsigned x, unsigned Version) { register unsigned y(x); Pos = y, Qry(Ver[Version], 1, n); while (y ^ ActVal) { y = ActVal, Pos = y, Qry(Ver[Version], 1, n); } Pos = x, Chg(Ver[Version - 1], Ver[Version], 1, n); return ActVal; } int main() { n = RD(), m = RD(); Ver[0] = Build(1, n); for (register unsigned i(1); i <= m; ++i) { A = RD(); switch (A) { case 1: { Ver[i] = ++CntN, Ver[i]->LS = Ver[i - 1]->LS, Ver[i]->RS = Ver[i - 1]->RS; A = Find(RD(), i), ActVal = Find(RD(), i), Pos = A; Chg(Ver[i - 1], Ver[i], 1, n); break; } case 2:{ Ver[i] = Ver[RD()]; break; } case 3:{ Ver[i] = ++CntN, Ver[i]->LS = Ver[i - 1]->LS, Ver[i]->RS = Ver[i - 1]->RS; A = Find(RD(), i); B = Find(RD(), i); printf("%u\n", (A == B)); break; } } } return Wild_Donkey; }
下載完全體資料之後, 發現本地時間花了 \(76s\), 非常的感人這還是加了路徑壓縮的 (至於很多題解表示不能使用路徑壓縮我看到之後表示很震驚, 我寫的難道壓縮的是空氣嗎), 所以考慮路徑壓縮 + 按秩合併, 將一個集合的歷史最深值作為秩 (因為歷史最深比當前深度好維護, 本以為這樣複雜度不對, 但是沒想到過了), 話說這是我第一次寫按秩合併, 不知是否繞了彎路.
unsigned a[10005], m, n, Cnt(0), A, B, C, Pos, ActVal, ActDeep, t, Ans(0), Tmp(0); char b[10005]; struct Node { Node *LS, *RS; unsigned Val, Dep; }N[4000005], *Ver[200005], *CntN(N), *Found; Node *Build(unsigned L, unsigned R) { register Node *x(++CntN); if(L ^ R) { register unsigned Mid((L + R) >> 1); x->LS = Build(L, Mid); x->RS = Build(Mid + 1, R); } else { x->Val = ++Cnt; x->Dep = 1; } return x; } void Chg(Node *x, Node *y, unsigned L, unsigned R) { if(L ^ R) { register unsigned Mid((L + R) >> 1); if(Pos <= Mid) { if(y->LS == x->LS) y->LS = ++CntN, y->LS->LS = x->LS->LS, y->LS->RS = x->LS->RS; Chg(x->LS, y->LS, L, Mid); } else { if(y->RS == x->RS) y->RS = ++CntN, y->RS->LS = x->RS->LS, y->RS->RS = x->RS->RS; Chg(x->RS, y->RS, Mid + 1, R); } } else { y->Val = ActVal; y->Dep = max(y->Dep, 1 + ActDeep); } } void Qry(Node *x, unsigned L, unsigned R) { if(L ^ R) { register unsigned Mid((L + R) >> 1); if(Pos <= Mid) Qry(x->LS, L, Mid); else Qry(x->RS, Mid + 1, R); } else Found = x; } unsigned Find(unsigned x, unsigned Version) { register unsigned y(x); Pos = y, Qry(Ver[Version], 1, n); while (y ^ Found->Val) y = Found->Val, Pos = y, Qry(Ver[Version], 1, n); Pos = x, ActVal = y, ActDeep = Found->Dep, Chg(Ver[Version - 1], Ver[Version], 1, n); return y; } int main() { n = RD(), m = RD(); Ver[0] = Build(1, n); for (register unsigned i(1); i <= m; ++i) { A = RD(); switch (A) { case 1: { Ver[i] = ++CntN, Ver[i]->LS = Ver[i - 1]->LS, Ver[i]->RS = Ver[i - 1]->RS; Find(RD(), i), A = Found->Val, B = Found->Dep; Find(RD(), i); if(B < Found->Dep) Pos = A, ActVal = Found->Val, ActDeep = B; else ActVal = A, Pos = Found->Val, ActDeep = Found->Dep; Chg(Ver[i - 1], Ver[i], 1, n); break; } case 2:{ Ver[i] = Ver[RD()]; break; } case 3:{ Ver[i] = ++CntN, Ver[i]->LS = Ver[i - 1]->LS, Ver[i]->RS = Ver[i - 1]->RS; A = Find(RD(), i), B = Find(RD(), i); printf("%u\n", (A == B)); break; } } } return Wild_Donkey; }