1. 程式人生 > 其它 >關於可持久化並查集

關於可持久化並查集

考慮並查集的過程,\(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;
}