關於可持久化並查集
阿新 • • 發佈:2022-03-01
考慮並查集的過程,\(fa_x=y\) 彷彿是不可逆的。你考慮這是一個賦值,且每次操作只會實行一次(usually),發現這可以可持久化。於是用主席樹維護這個 \(fa\) 即可。
但由於你直接跳 \(fa\) 是 \(\mathcal {O}(n)\),又不能改變 \(fa\) 的形態,於是只能用按秩合併,然後查根是 \(\mathcal{O}(log_2^2 n)\) 的。板子:
#include <cstdio> #include <algorithm> #include <cmath> #include <cstring> #include <vector> #define ls tree[p].L #define rs tree[p].R using namespace std; const int MAXN = 2e5 + 5; struct Node { int L, R; }tree[MAXN * 25]; int n, m, now, root[MAXN], dep[MAXN * 25], fa[MAXN * 25], tot; int ask(int p, int l, int r, int x) { // 查詢位置 x在時態 t下對應線段樹的編號 if(l == r) return p; int mid = (l + r) >> 1; return x <= mid ? ask(ls, l, mid, x) : ask(rs, mid + 1, r, x); } void build(int &p, int l, int r) { // 建好樹,弄好 dep,fa p = ++ tot; if(l == r) { dep[p] = 1; fa[p] = l; return; } int mid = (l + r) >> 1; build(ls, l, mid); build(rs, mid + 1, r); } int fnd(int x) { int p = ask(root[now], 1, n, x); return fa[p] ^ x ? fnd(fa[p]) : x; } // x 為實際編號, p 為 smt對應的編號 void add(int &p, int q, int l, int r, int x, int y, int f) { if(!f) p = ++ tot, tree[p] = tree[q], dep[p] = dep[q], fa[p] = fa[q]; // 注意 dep,fa 也要搞過來 // 另外加上 !f 卡常(基本少建 1/2的點) if(l == r) { if(!f) fa[p] = y; else dep[p] = y; return; } int mid = (l + r) >> 1; if(x <= mid) add(ls, tree[q].L, l, mid, x, y, f); else add(rs, tree[q].R, mid + 1, r, x, y, f); } void makefa(int u, int v, int p, int q) { // u,v 下標 p,q 對應編號 int r = dep[p] == dep[q] ? dep[q] + 1 : dep[q]; now ++; add(root[now], root[now - 1], 1, n, u, v, 0); add(root[now], root[now], 1, n, v, r, 1); } void mrg(int x, int y) { int u = fnd(x), v = fnd(y); if(u == v) { now ++; root[now] = root[now - 1]; return; } // 注意這裡 root 也要搞過來 int p = ask(root[now], 1, n, u), q = ask(root[now], 1, n, v); if(dep[p] <= dep[q]) makefa(u, v, p, q); else makefa(v, u, q, p); } int main() { int f, l, r, x; scanf("%d%d", &n, &m); build(root[0], 1, n); for(int i = 1; i <= m; i ++) { scanf("%d", &f); if(f == 1) scanf("%d%d", &l, &r), mrg(l, r); else if(f == 2) scanf("%d", &x), root[++ now] = root[x]; else now ++, root[now] = root[now - 1], scanf("%d%d", &l, &r), printf("%d\n", fnd(l) == fnd(r)); } return 0; }