洛谷P3402 可持久化并查集 题解
题目链接:https://www.luogu.com.cn/problem/P3402
题目大意:
给定 \(n\) 个集合,第 \(i\) 个集合内初始状态下只有一个数,为 \(i\)。
有 \(m\) 次操作。操作分为 \(3\) 种:
-
1 a b
合并 \(a,b\) 所在集合; -
2 k
回到第 \(k\) 次操作(执行三种操作中的任意一种都记为一次操作)之后的状态; -
3 a b
询问 \(a,b\) 是否属于同一集合,如果是则输出 \(1\) ,否则输出 \(0\)。
解题思路:
可持久化并查集。并查集 按秩合并,不要路径压缩。
示例程序:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
struct Tree {
int l, r,val;
} tree[maxn*80];
int tot, rootfa[maxn*2], rootdep[maxn*2];
int n, m;
void build(int l, int r, int& rt) {
rt = ++tot;
if (l == r) {
tree[rt].val = l;
return;
}
int mid = (l + r) / 2;
build(l, mid, tree[rt].l);
build(mid+1, r, tree[rt].r);
}
void update(int p, int val, int l, int r, int pt, int &rt) {
tree[rt = ++tot] = tree[pt];
if (l == r) {
tree[rt].val = val;
return;
}
int mid = (l + r) / 2;
if (p <= mid)
update(p, val, l, mid, tree[pt].l, tree[rt].l);
else
update(p, val, mid+1, r, tree[pt].r, tree[rt].r);
}
int query(int p, int l, int r, int rt) {
if (l == r) return tree[rt].val;
int mid = (l + r) / 2;
if (p <= mid)
return query(p, l, mid, tree[rt].l);
else
return query(p, mid+1, r, tree[rt].r);
}
int ffind(int ver, int x) {
int fa = query(x, 1, n, rootfa[ver]);
return fa == x ? x : ffind(ver, fa);
}
void funion(int ver, int x, int y) {
x = ffind(ver-1, x);
y = ffind(ver-1, y);
if (x == y) {
rootfa[ver] = rootfa[ver-1];
rootdep[ver] = rootdep[ver-1];
}
else {
int depx = query(x, 1, n, rootdep[ver-1]);
int depy = query(y, 1, n, rootdep[ver-1]);
if (depx > depy) swap(x, y);
update(x, y, 1, n, rootfa[ver-1], rootfa[ver]);
if (depx == depy) {
update(y, depy+1, 1, n, rootdep[ver-1], rootdep[ver]);
}
else {
rootdep[ver] = rootdep[ver-1];
}
}
}
int main() {
ios::sync_with_stdio(0);
cin >> n >> m;
build(1, n, rootfa[0]);
for (int i = 1; i <= m; i ++) {
int op, k, a, b;
cin >> op;
if (op == 2) cin >> k;
else cin >> a >> b;
if (op == 1) { // 合并a,b所在集合
funion(i, a, b);
}
else if (op == 2) { // 回到第k次操作
rootfa[i] = rootfa[k];
rootdep[i] = rootdep[k];
}
else { // 询问a,b是否属于同一集合
rootfa[i] = rootfa[i-1];
rootdep[i] = rootdep[i-1];
cout << (ffind(i, a) == ffind(i, b) ? 1 : 0) << endl;
}
}
return 0;
}