可撤销并查集

用途

  • 顾名思义, 可以将以前的 Merge 操作撤销
  • 但是只能从后往前一步一步撤销, 做不到像主席树一样直接访问历史版本
  • 多用于树上问题

原理

  • 按秩合并
    • 将集合 x 和 y 合并时, 如果 siz[x] > siz[y], 那么就把 y 的父亲设为 x, 反之亦然
    • (和启发式合并有点像)时间复杂度 O(logn)
  • 用栈维护操作序列

过程

  • 数组
int s[N], si[N], cnt; // 记录 父亲, 集合大小, 用于初始化
int st[N], tp; // 栈, 栈顶下标
  • Find
int Find(int x){
if(x != s[x]){
return Find(s[x]); // 不能路径压缩, 否则就没法撤销了
}
return s[x];
}
  • Merge
void Merge(int x, int y){
int fa = Find(x);
int fb = Find(y);
if(fa == fb){
return;
}
if(si[fa] < si[fb]){ // 小的接在大的下面
swap(fa, fb);
}
s[fb] = fa;
si[fa] += si[fb]; // 更新较大的集合的 size
st[++tp] = fb; // 记录该步操作的较小集合, 用于撤销
}
  • Delete
void Delete(int x){
while(tp > x){ // 目标是撤销到第 x 步操作
int k = st[tp];
si[s[k]] -= si[k]; // 更新较大集合的 size
s[k] = k; // 将较小集合分离出来
tp--;
}
}

完整代码

// Union ans Find Set
struct Ufs{
int s[N], si[N], cnt;
int st[N], tp;
inline void Insert(){
++cnt;
s[cnt] = cnt;
si[cnt] = 1;
}
int Find(int x){
if(x != s[x]){
return Find(s[x]);
}
return s[x];
}
void Merge(int x, int y){
int fa = Find(x);
int fb = Find(y);
if(fa == fb){
return;
}
if(si[fa] < si[fb]){
swap(fa, fb);
}
s[fb] = fa;
si[fa] += si[fb];
st[++tp] = fb;
}
void Delete(int x){
while(tp > x){
int k = st[tp];
si[s[k]] -= si[k];
s[k] = k;
tp--;
}
}
}s;
posted on   Bubble_e  阅读(419)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话



点击右上角即可分享
微信分享提示