可持久化并查集,就是用可持久化线段树维护每个版本每个节点的父亲,这样显然是不能路径压缩的,否则我们需要恢复太多状态。
但是这并不影响我们启发式合并,于是,每次把深度小的连通块向深度大的上并就好了。
#include <cstdio>
#define re register
inline int read(){
int s = 0, w = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-')w = -1;ch = getchar();}
while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0',ch = getchar();
return s * w;
}
const int MAXN = 100010;
const int MAXM = 200010;
struct SegTree{
int lc, rc, val, dep;
}t[MAXN * 20];
int cnt, opt, a, b, root[MAXM], rt, n, m;
int build(int l, int r){
int id = ++cnt;
if(l == r){
t[id].val = l;
return id;
}
int mid = (l + r) >> 1;
t[id].lc = build(l, mid);
t[id].rc = build(mid + 1, r);
return id;
}
int query(int now, int l, int r, int x){
if(l == r) return now;
int mid = (l + r) >> 1;
return x <= mid ? query(t[now].lc, l, mid, x) : query(t[now].rc, mid + 1, r, x);
}
int update(int now, int l, int r, int x, int p){
int id = ++cnt;
t[id] = t[now];
if(l == r){ t[id].val = p; return id; }
int mid = (l + r) >> 1;
if(x <= mid) t[id].lc = update(t[now].lc, l, mid, x, p);
else t[id].rc = update(t[now].rc, mid + 1, r, x, p);
return id;
}
int find(int x){
int now = query(rt, 1, n, x);
if(t[now].val == x) return now;
return find(t[now].val);
}
void updep(int now, int l, int r, int x){
if(l == r){ ++t[now].dep; return; }
int mid = (l + r) >> 1;
if(x <= mid) updep(t[now].lc, l, mid, x);
else updep(t[now].rc, mid + 1, r, x);
}
int main(){
n = read(); m = read();
root[0] = build(1, n);
for(int i = 1; i <= m; ++i){
opt = read();
rt = root[i - 1];
if(opt == 1){
a = read(); b = read();
int fa = find(a), fb = find(b);
if(t[fa].dep < t[fb].dep){
root[i] = update(root[i - 1], 1, n, t[fa].val, t[fb].val);
if(t[fa].dep == t[fb].dep) updep(root[i], 1, n, t[fb].val);
}
else{
root[i] = update(root[i - 1], 1, n, t[fb].val, t[fa].val);
if(t[fa].dep == t[fb].dep) updep(root[i], 1, n, t[fa].val);
}
}
if(opt == 2){
rt = root[i] = root[read()];
}
if(opt == 3){
rt = root[i] = root[i - 1];
printf("%d
", t[find(read())].val == t[find(read())].val);
}
}
return 0;
}