题解 P3402 【【模板】可持久化并查集】
首先我感觉其他的题解都或多或少出现了一些小问题,这里来发一篇比较正确的题解。
首先可持久化并查集我们是用主席树来维护的,维护
那么就可以进行可持久化了。
既然取消了路径压缩,为了保证复杂度,我们需要进行按秩合并。
按秩合并其实就是,我们以树高为秩,每次将树高低的树合并到树高高的树上,那么对于树高高的树来说树高并没有变(也就说原树访问根节点的代价并没有变),而树高低的树访问根节点的代价只增加了
还有一种特殊情况那就是两棵树树高相同。这个时候把树
所以只需要改动一个点,并不是@TPLY所说的改动一群点(他的代码里写的也是只改一个点的代码,自相矛盾)。
也不是@pengym所说的不可能有
(由于一开始写代码的时候没理解清楚,变量名有些混乱,rta,rtb其实是a点和b点在主席树里的编号,depth指的是树高)。
#include <bits/stdc++.h>
const int N = 1e5 + 10;
int n, m, i, j, k, cntu;
int fa[N << 7], depth[N << 7], root[N << 1], lc[N << 7], rc[N << 7];
int build(int l, int r) {
int u = ++cntu;
if (l == r) { fa[u] = l; return u; }
int mid = (l + r) >> 1;
lc[u] = build(l, mid);
rc[u] = build(mid + 1, r);
return u;
}
int query(int u, int l, int r, int x) {
if (l == r) return u;
int mid = (l + r) >> 1;
if (x <= mid) return query(lc[u], l, mid, x);
else return query(rc[u], mid + 1, r, x);
}
int update(int pre, int x, int y, int l, int r) {
int u = ++cntu; lc[u] = lc[pre], rc[u] = rc[pre];
if (l == r) {
fa[u] = y;
depth[u] = depth[pre];
return u;
}
int mid = (l + r) >> 1;
if (x <= mid) lc[u] = update(lc[pre], x, y, l, mid);
else rc[u] = update(rc[pre], x, y, mid + 1, r);
return u;
}
void add(int u, int l, int r, int x) {
if (l == r) { depth[u]++; return; }
int mid = (l + r) >> 1;
if (x <= mid) add(lc[u], l, mid, x);
else add(rc[u], mid + 1, r, x);
}
int find(int v, int x) {
int y = query(root[v], 1, n, x);
if (fa[y] == x) return y;
return find(v, fa[y]);
}
int main() {
scanf("%d %d", &n, &m);
root[0] = build(1, n);
for (int i = 1, op, a, b, v; i <= m; i++) {
scanf("%d", &op), root[i] = root[i - 1];
if (op == 1) {
scanf("%d %d", &a, &b);
int rta = find(i, a), rtb = find(i, b);
if (depth[rta] > depth[rtb]) std::swap(rta, rtb);
if (fa[rta] == fa[rtb]) continue;
root[i] = update(root[i - 1], fa[rta], fa[rtb], 1, n);
if (depth[rta] == depth[rtb]) add(root[i], 1, n, fa[rtb]);
} else if (op == 2) scanf("%d", &v), root[i] = root[v];
else {
scanf("%d %d", &a, &b);
int rta = find(i, a), rtb = find(i, b);
puts(fa[rta] == fa[rtb] ? "1" : "0");
}
}
return 0;
}