给定 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;
}