P3402 可持久化并查集

思路

  • 尽管这道题想让我们在线做, 但是我还是想离线哈哈
  • 题目中既有撤销(回到第 k 个版本), 又涉及并查集, 想到用可撤销并查集
    • 如果我们按照一般思路撤销来做, 就不得不将每个历史版本存一遍(很明显不可能)
  • 如果第 i 次操作让我们回到第 k 次操作, 那我们何不在第 k 次操作进行完之后接着就进行第 i 次操作呢
    • 给操作序列建图
      • 对于 opt 1, 3, 由 i - 1 向 i 连边
      • 对于 opt 2, 由 k 向 i 连边
    • 然后从 0 节点开始 Dfs
  • Dfs 之前记录下当前状态(并查集合并了几步), 遍历完所有子树之后将并查集恢复到初始状态, 然后返回

代码

# include <bits/stdc++.h>
# define int long long
using namespace std;
const int N = 2e5 + 10;
int n, m;
int opt[N], a[N], b[N], k[N], ans[N];
int si[N];
vector <int> e[N];
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;
void Dfs(int x){
si[x] = s.tp;
if(opt[x] == 1ll){
s.Merge(a[x], b[x]);
}else if(opt[x] == 3ll){
ans[x] = (s.Find(a[x]) == s.Find(b[x]));
}
for(auto i : e[x]){
Dfs(i);
}
s.Delete(si[x]);
}
signed main(){
// freopen("1.in", "r", stdin);
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin >> n >> m;
for(int i = 1; i <= n; i++){
s.Insert();
}
for(int i = 1; i <= m; i++){
cin >> opt[i];
if(opt[i] == 1){
cin >> a[i] >> b[i];
e[i - 1].push_back(i);
}else if(opt[i] == 2){
cin >> k[i];
e[k[i]].push_back(i);
}else{
cin >> a[i] >> b[i];
e[i - 1].push_back(i);
}
}
Dfs(0);
for(int i = 1; i <= m; i++){
if(opt[i] == 3){
cout << ans[i] << "\n";
}
}
}
posted on   Bubble_e  阅读(26)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话



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