带撤销并查集支持从某个元素从原来的集合中撤出来,然后加入到一个另外一个集合中,或者删除该元素
用一个映射来表示元素和并查集中序号的关系,代码中用(to[x]) 表示x号元素在并查集中的 id
删除 x 号元素时,需要将 (to[x]) 的集合大小减去1,然后令 (to[x]=-1) 标记 x 删除即可
如果要重新加入一个元素,那么给x分配一个新的 id,(to[x] = newid)
例题1:https://www.cometoj.com/contest/33/problem/E?problem_id=1459
#include <bits/stdc++.h>
using namespace std;
const int N = 4000010;
int fa[N],to[N],sz[N],cnt,a[N],b[N],tot;
int n,m,k,x,y;
int ok[N];
int find(int x){
return x == fa[x]?x:fa[x] = find(fa[x]);
}
void merge(int x,int y){
int a = find(to[x]);
int b = find(to[y]);
if(a==b) return;
fa[b] = a;
sz[a] += sz[b]; sz[b] = 0;
}
// 将x从原集合删除,加入到 y 所属的集合
void update(int x,int y){
sz[find(to[x])]--;
to[x] = ++cnt;//给x分配新的 id
sz[cnt] = 1;
fa[cnt] = cnt;
merge(x,y);
}
int main(){
scanf("%d%d",&n,&m);cnt = n;
for(int i=1;i<=n;i++)fa[i] = i,to[i] = i,sz[i] = 1;
for(int i=1;i<=m;i++){
scanf("%d%d",&k,&x);if(k!=3)scanf("%d",&y);
if(k == 5){
a[++tot] = x;
b[tot] = y;continue;
}
if(k == 1)merge(x,y);
if(k == 2)update(x,y);
if(k == 4){
if(find(to[x]) == find(to[y]))printf("Yes
");
else printf("No
");
}
if(k == 3)printf("%d
",sz[find(to[x])]-1);
}
for(int i=1;i<=tot;i++){
if(find(to[a[i]]) == find(to[b[i]])) ok[find(to[a[i]])] = 1;
}
int res = -1;
for(int i=1;i<=n;i++)
if(!ok[find(to[i])])res = max(res,sz[find(to[i])]);
cout<<res<<endl;
return 0;
}
例题2:https://nanti.jisuanke.com/t/42576
[2019南昌区域赛A]
不要求在线的可持久化操作,可以离线处理询问,按照正常的时间顺序维护并查集,由于需要回溯,所以不能使用路径压缩
复杂度 (O(mlog n))
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
#define dbg(x...) do { cout << "