窝太菜了只配浅谈呃呃呃
前置芝士
主席树& 并查集
啊
可持久化并查集 , 差不多可能大概应该就是在主席树上做并查集 , 用主席树维护每个点的父亲...
修改
每次新建一个节点
值得一提的是 , 如果用路径压缩会TM双LE2333 , 这里只能按秩合并 即每次把深度小的并查集合并到深度高的并查集中去(如果两个深度相同 , 则其中一个并查集深度++ ) 可以画图感性理解一下
查询
查询该点在当前版本中的位置
感觉讲的好苍白呃呃呃 , 还是直接看我的代码(垃圾堆)自行理解吧呃呃呃
#include <bits/stdc++.h>
using namespace std;
#define mid (l + r) / 2
const int N = 200005;
int n, m, cnt;
int deep[N * 30], f[N * 30]; // deep记录的是每个点的深度 , f记录的是父亲
int T[N * 30], L[N * 30], R[N * 30]; //这里试了几次....开N << 5就炸了(为什么是TLE) 30倍应该刚刚好
int build(int l, int r) {
int rt = ++cnt;
if(l == r) {
f[rt] = l;
return rt;
}
L[rt] = build(l, mid);
R[rt] = build(mid + 1, r);
return rt;
}
int updata(int pre, int l, int r, int ppx, int k) { // pre为上一个版本的当前节点(感性理解233) , ppx为待修改的位置, k为修改的值
int rt = ++cnt;
L[rt] = L[pre]; R[rt] = R[pre];
if(l == r) {
f[rt] = k;
deep[rt] = deep[pre]; // 别忘了继承这个深度
return rt;
}
if(ppx <= mid) L[rt] = updata(L[pre], l, mid, ppx, k);
else R[rt] = updata(R[pre], mid + 1, r, ppx, k);
return rt;
}
int query(int u, int l, int r, int ppx) { // u为当前节点编号
if(l == r) return u;
if(ppx <= mid) return query(L[u], l, mid, ppx);
else return query(R[u], mid + 1, r, ppx);
}
void add(int u, int l, int r, int ppx) {//增加当前并查集的深度
if(l == r) {
deep[u]++;
return ;
}
if(l < r) {
if(ppx <= mid) add(L[u], l, mid, ppx);
else add(R[u], mid + 1, r, ppx);
}
}
int find(int u, int x) {
int fx = query(u, 1, n, x);
if(x == f[fx]) return fx;
return find(u, f[fx]);
}
int main() {
ios :: sync_with_stdio(0);
cin >> n >> m;
T[0] = build(1, n);
for(int i = 1; i <= m; i++) {
int opt, a, b;
cin >> opt;
if(opt == 1) {
cin >> a >> b;
T[i] = T[i - 1];
int fa = find(T[i], a);
int fb = find(T[i], b);
if(f[fa] == f[fb]) continue;
if(deep[fa] > deep[fb]) swap(fa, fb);
T[i] = updata(T[i - 1], 1, n, f[fa], f[fb]);
if(deep[fa] == deep[fb]) add(T[i], 1, n, f[fb]);
}
else if(opt == 2) {
cin >> a;
T[i] = T[a];
}
else {
cin >> a >> b;
T[i] = T[i - 1];
if(f[find(T[i], a)] == f[find(T[i], b)]) cout << 1 << endl;
else cout << 0 << endl;
}
}return 0;
}
有什么问题可以直接在评论区告诉窝的鹅鹅鹅
不会吧不会吧不会真有人看的叭