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 \ lastans\),\(lastans\) 表示上一个询问的答案,其初始值为 \(0\) 。
输出格式
对于每个询问操作,输出一个结果,每个结果占一行。
数据范围
\(0<n, m \leq 2 \times 10^{5}\)
输入样例:
5 6
1 1 2
3 1 2
2 1
3 0 3
2 1
3 1 2
输出样例:
1
0
1
解题思路
可持久化并查集
类似于可持久化数组,可持久化并查集关键在于维护每一个版本的某个被改变的节点的信息,如果并查集采用路径压缩,则需要修改的节点可能过多,时间和空间可能不允许,所以采用按秩合并的优化方法,即使并查集按秩合并后形成的树形结构接近完全树的形态,树高为 \(O(logn)\) 级别,这样查询时暴力向上查询即可,这里采用按秩合并中按照高度合并的方法,即每次合并时高度较高的作为根节点。线段树上维护两个信息:父节点和该节点的高度。每次操作都在对应版本上结合线段树像普通并查集的操作进行即可,注意合并时两棵树的高度如果相等,需要增加作为根节点的那棵树的高度
- 时间复杂度:\(O(mlog^2n)\)
代码
// 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;
}