270. 可持久化并查集加强版

题目链接

270. 可持久化并查集加强版

n 个集合, m 个操作,操作分为三种:

  • 1 a b ——合并 a,b 所在集合;
  • 2 k ——回到输入的第 k 次操作之后的状态;
  • 3 a b ——询问 a,b 是否属于同一集合,是则输出 1 否则输出 0 。

输入格式

第一行为 n,m
接下来 m 行描述了每个操作,按照题目描述中所述的格式。
每个操作强制在线,需要对输入的 a,b,k 进行运算得到真实的 a,b,k 后再执行操作,运算方法为 x=x xor lastanslastans 表示上一个询问的答案,其初始值为 0

输出格式

对于每个询问操作,输出一个结果,每个结果占一行。

数据范围

0<n,m2×105

输入样例:

5 6 1 1 2 3 1 2 2 1 3 0 3 2 1 3 1 2

输出样例:

1 0 1

解题思路

可持久化并查集

类似于可持久化数组,可持久化并查集关键在于维护每一个版本的某个被改变的节点的信息,如果并查集采用路径压缩,则需要修改的节点可能过多,时间和空间可能不允许,所以采用按秩合并的优化方法,即使并查集按秩合并后形成的树形结构接近完全树的形态,树高为 O(logn) 级别,这样查询时暴力向上查询即可,这里采用按秩合并中按照高度合并的方法,即每次合并时高度较高的作为根节点。线段树上维护两个信息:父节点和该节点的高度。每次操作都在对应版本上结合线段树像普通并查集的操作进行即可,注意合并时两棵树的高度如果相等,需要增加作为根节点的那棵树的高度

  • 时间复杂度:O(mlog2n)

代码

// Problem: 可持久化并查集加强版 // Contest: AcWing // URL: https://www.acwing.com/problem/content/272/ // Memory Limit: 128 MB // Time Limit: 1000 ms // // Powered by CP Editor (https://cpeditor.org) // %%%Skyqwq #include <bits/stdc++.h> //#define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; typedef pair<LL, LL> PLL; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } const int N=2e5+5; int n,m,cnt,root[N]; struct T { int l,r; int fa,dep; }tr[N*25]; int build(int l,int r) { int p=++cnt; if(l==r) { tr[p].fa=l; tr[p].dep=1; return p; } int mid=l+r>>1; tr[p].l=build(l,mid); tr[p].r=build(mid+1,r); return p; } PII ask(int u,int x,int l,int r) { if(l==r)return {tr[u].fa,tr[u].dep}; int mid=l+r>>1; if(x<=mid)return ask(tr[u].l,x,l,mid); return ask(tr[u].r,x,mid+1,r); } PII find(int u,int x) { PII t=ask(u,x,1,n); if(x==t.fi)return t; return find(u,t.fi); } int update(int u,int x,int y,int l,int r) { int p=++cnt; tr[p]=tr[u]; if(l==r) { tr[p].fa=y; return p; } int mid=l+r>>1; if(x<=mid)tr[p].l=update(tr[u].l,x,y,l,mid); else tr[p].r=update(tr[u].r,x,y,mid+1,r); return p; } void add(int u,int x,int l,int r) { if(l==r) { tr[u].dep++; return ; } int mid=l+r>>1; if(x<=mid)add(tr[u].l,x,l,mid); else add(tr[u].r,x,mid+1,r); } int main() { read(n),read(m); root[0]=build(1,n); int lst=0; for(int i=1;i<=m;i++) { int op,a,b,k; read(op); root[i]=root[i-1]; if(op==1) { read(a),read(b); a^=lst,b^=lst; PII t1=find(root[i],a); PII t2=find(root[i],b); if(t1.fi==t2.fi) continue; if(t1.se<t2.se)swap(t1,t2); root[i]=update(root[i-1],t2.fi,t1.fi,1,n); if(t1.se==t2.se)add(root[i],t1.fi,1,n); } else if(op==2) { read(k); k^=lst; root[i]=root[k]; } else { read(a),read(b); a^=lst,b^=lst; PII t1=find(root[i],a),t2=find(root[i],b); putchar(t1.fi==t2.fi?'1':'0'); lst=t1.fi==t2.fi; putchar('\n'); } } return 0; }

__EOF__

本文作者acwing_zyy
本文链接https://www.cnblogs.com/zyyun/p/16199732.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zyy2001  阅读(36)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示