题目大意:
要求维护三种操作。
1
x
y
1\ x\ y
1 x y:合并
x
x
x和
y
y
y所在集合。
2
k
2\ k
2 k:回到第
k
k
k个操作后的状态。
3
x
y
3\ x\ y
3 x y:询问
x
x
x和
y
y
y是否在一个集合。
强制在线。
n
,
q
≤
2
∗
1
0
5
n,q≤2*10^5
n,q≤2∗105
分析:
如果不考虑路径压缩,使用启发式合并(也就是把小的集合合并到大的集合中)。那么每次只有一个位置的
f
a
fa
fa发生了改变,其他位置都没有变。
由于要可持久化,考虑使用一棵线段树,除了叶子节点外所有点权值都为
0
0
0,叶子节点记录其父亲的节点编号。那么我们可以对当前线段树做一次询问是
l
o
g
n
logn
logn的,而跳父亲不超过
l
o
g
n
logn
logn步(每次集合至少增大一倍),所有
g
e
t
f
a
getfa
getfa是
O
(
l
o
g
2
n
)
O(log^2n)
O(log2n)的。
我们再开一个主席树维护一下
s
i
z
e
size
size即可。
总复杂度是
O
(
n
l
o
g
2
n
)
O(nlog^2n)
O(nlog2n)的。
代码:
/**************************************************************
Problem: 3674
User: ypxrain
Language: C++
Result: Accepted
Time:2052 ms
Memory:237236 kb
****************************************************************/
#include <iostream>
#include <cstdio>
#include <cmath>
const int maxn=2e5+7;
using namespace std;
int n,m,ans,op,x,y;
struct tree{
struct node{
int l,r,data;
}t[maxn*50];
int root[maxn],cnt;
void build(int &p,int l,int r,int k)
{
if (!p) p=++cnt;
if (l==r)
{
t[p].data=k;
return;
}
int mid=(l+r)/2;
build(t[p].l,l,mid,k);
build(t[p].r,mid+1,r,k);
}
void ins(int &p,int q,int l,int r,int x,int k)
{
if (!p) p=++cnt;
if (l==r)
{
t[p].data=k;
return;
}
int mid=(l+r)/2;
if (x<=mid) t[p].r=t[q].r,ins(t[p].l,t[q].l,l,mid,x,k);
else t[p].l=t[q].l,ins(t[p].r,t[q].r,mid+1,r,x,k);
}
int query(int p,int l,int r,int x)
{
if (l==r) return t[p].data;
int mid=(l+r)/2;
if (x<=mid) return query(t[p].l,l,mid,x);
else return query(t[p].r,mid+1,r,x);
}
}A,B;
int getfa(int p,int x)
{
int u=A.query(A.root[p],1,n,x);
if (!u) return x;
return getfa(p,u);
}
int main()
{
scanf("%d%d",&n,&m);
A.build(A.root[0],1,n,0);
B.build(B.root[0],1,n,1);
for (int i=1;i<=m;i++)
{
scanf("%d",&op);
if (op==1)
{
scanf("%d%d",&x,&y);
x^=ans,y^=ans;
int u=getfa(i-1,x),v=getfa(i-1,y);
if (u==v)
{
A.root[i]=A.root[i-1],B.root[i]=B.root[i-1];
}
else
{
int nu=B.query(B.root[i-1],1,n,u);
int nv=B.query(B.root[i-1],1,n,v);
if (nu>nv) swap(u,v);
A.ins(A.root[i],A.root[i-1],1,n,u,v);
B.ins(B.root[i],B.root[i-1],1,n,v,nu+nv);
}
}
if (op==2)
{
scanf("%d",&x);
x^=ans;
A.root[i]=A.root[x],B.root[i]=B.root[x];
}
if (op==3)
{
scanf("%d%d",&x,&y);
x^=ans,y^=ans;
A.root[i]=A.root[i-1],B.root[i]=B.root[i-1];
int u=getfa(i-1,x),v=getfa(i-1,y);
if (u==v) ans=1;
else ans=0;
printf("%d\n",ans);
}
}
}