其实这已经快不能算并查集了。
维护可持久化的fa数组,因为不能路径压缩,所以就用按秩合并(启发式合并)就好了。
可持久化数组一个log,启发式合并一个log,总复杂度就是
O(nlog2n)
O
(
n
l
o
g
2
n
)
了。
Code
#include <iostream>
#include <cstring>
#include <cstdio>
const int maxn = 2e5 + 7;
using namespace std;
int n, m;
int root[maxn];
struct node {
int fa;
int dep; //启发式合并用,也可以写size
int l, r;
} st[maxn * 30];
int cnt;
inline int read()
{
int X = 0; char ch = getchar();
while (ch < '0' || ch > '9') ch = getchar();
while (ch >= '0' && ch <= '9') X = X * 10 + ch - '0', ch = getchar();
return X;
}
void build(int l, int r, int &rt)
{
rt = ++cnt;
if (l == r) {
st[rt].fa = l;
st[rt].dep = 0;
return;
}
int mid = l + r >> 1;
build(l, mid, st[rt].l);
build(mid + 1, r, st[rt].r);
}
void update(int x, int y, int &rt, int l, int r)
{
st[++cnt] = st[rt];
rt = cnt;
if (l == r) {
st[rt].fa = y;
return;
}
int mid = l + r >> 1;
if (x <= mid) update(x, y, st[rt].l, l, mid);
else update(x, y, st[rt].r, mid + 1, r);
}
int query(int x, int l, int r, int rt)
{
if (l == r) return rt;
int mid = l + r >> 1;
if (x <= mid) return query(x, l, mid, st[rt].l);
else return query(x, mid + 1, r, st[rt].r);
}
void add(int x, int rt, int l, int r)
{
if (l == r) {
st[rt].dep++;
return;
}
int mid = l + r >> 1;
if (x <= mid) add(x, st[rt].l, l, mid);
else add(x, st[rt].r, mid + 1, r);
}
int find(int id, int x)
{
int f = query(x, 1, n, root[id]);
if (st[f].fa == x) return f;
return find(id, st[f].fa);
}
int main(void)
{
cin >> n >> m;
build(1, n, root[0]);
for (int i = 1; i <= m; i++) {
int opt = read(), x = read();
if (opt == 1) {
root[i] = root[i - 1];
int y = read(), u = find(i, x), v = find(i, y);
if (u == v) continue;
if (st[u].dep > st[v].dep) swap(u, v);
update(st[u].fa, st[v].fa, root[i], 1, n);
if (st[u].dep == st[v].dep) add(st[v].fa, root[i], 1, n);
}
else if (opt == 2) root[i] = root[x];
else {
root[i] = root[i - 1];
int y = read(), u = find(i, x), v = find(i, y);
if (u == v) printf("1\n");
else printf("0\n");
}
}
return 0;
}