模板 可持久化并查集

给定 n n n 个集合,第 i i i 个集合内初始状态下只有一个数 i i i。现在有 m m m 次操作。

  • 1 a b \quad 合并 a a a b b b 所在集合
  • 2 k \quad 回到第 k k k 此操作的状态。
  • 3 a b \quad 询问 a a a b b b 是否在同一集合中。

类似于并查集的,我们要用主席树维护每个点的归属。请注意不要在合并父亲的时候进行路径压缩,否则在断边时,整个子树都要修改,复杂度会被卡到 O ( n 2 ) O(n^2) O(n2)

那么再考虑查找一个点的在哪个联通块中,暴力即可,显然可能退化成一条链,复杂度会被卡到 O ( n 2 ) O(n^2) O(n2)。所以再合并操作时就启发式的按秩合并,即为对于两个点,把深度小到加到深度大的。

至于撤销操作,将历史版本的根赋值给当前版本即可。

#include 
using namespace std;
const int N = 2e5 + 5, INF = 0x3f3f3f3f;
inline int read() {
	int x = 0, f = 0; char ch = 0;
	while (!isdigit(ch)) f |= ch == '-', ch = getchar();
	while (isdigit(ch)) x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
	return f ? -x : x;
}
#define ls t[now].l
#define rs t[now].r
struct SegTree {
	int l, r;
}t[N << 4];
int n, m, cnt;
int fa[N << 4], dep[N << 4], root[N];
void build(int &now, int l, int r)  {
	now = ++cnt;
	if (l == r) {
		fa[now] = l;
		return ;
	}
	int mid = (l + r) >> 1;
	build(ls, l, mid), build(rs, mid + 1, r);
}
void update(int &now, int lst, int l, int r, int x, int k) {//将 x 的父亲修改为 k 
	now = ++cnt;
	if (l == r) {
		fa[now] = k, dep[now] = dep[lst];
		return ;
	}
	ls = t[lst].l, rs = t[lst].r;
	int mid = (l + r) >> 1;
	if (x <= mid) update(ls, t[lst].l, l, mid, x, k);
	else update(rs, t[lst].r, mid + 1, r, x, k);
}
void add(int now, int l, int r, int x) { //将 x 所在连通块点的深度增加一 
	if (l == r) {
		dep[now]++;
		return ; 
	} 
	int mid = (l + r) >> 1;
	if (x <= mid) add(ls, l, mid, x);
	else add(rs, mid + 1, r, x);
}
int query(int now, int l, int r, int x) { //询问 x 的父亲 
	if (l == r) return now;
	int mid = (l + r) >> 1;
	if (x <= mid) return query(ls, l, mid, x);
	else return query(rs, mid + 1, r, x);
}
int find(int now, int x) {
	int k = query(now, 1, n, x);
	if (x == fa[k]) return k;
	return find(now, fa[k]); 
}
int main() {
	n = read(), m = read();
	build(root[0], 1, n);
	for (int i = 1; i <= m; i++) {
		int opt = read();
		if (opt == 1) {
			int a = read(), b = read();
			root[i] = root[i - 1];
			int x = find(root[i], a), y = find(root[i], b);
			if (fa[x] == fa[y]) continue; 
			if (dep[x] > dep[y]) swap(x, y);
			update(root[i], root[i - 1], 1, n, fa[x], fa[y]);
			if (dep[x] == dep[y]) add(root[i], 1, n, fa[y]);
		} else if (opt == 2) {
			int k = read();
			root[i] = root[k];
		} else {			
			int a = read(), b = read();
			root[i] = root[i - 1];
			int x = find(root[i], a), y = find(root[i], b); 
			if (fa[x] == fa[y]) puts("1");
			else puts("0");
		}
	}
	return 0;
}

你可能感兴趣的:(模板 可持久化并查集)