/*
可持久化并查集
并查集的本质就是数组,所以可持久化并查集就是可持久化数组
但是并查集的路径压缩不能用了,因为路径压缩会改变fa数组,
在可持久化里,改变就意味着log的空间复杂度,所以不用路径压缩,
改为秩优化,即将深度小的并到深度大的
所以需要维护两个可持久化数组,开两倍空间
*/
#include <iostream>
using namespace std;
const int maxn = 2e5 + 5;
int rootfa[maxn],rootdep[maxn],cnt,tot;
//记录版本为i的fa数组和dep数组的线段树的根,
//这里是用线段树模拟数组!!!
struct node{
int l,r,val;
}hjt[maxn*80];
int n;
void build(int l,int r,int &now) //并查集初始有值,所以需要初始化,dep则不用初始化
{
now = ++cnt;
if( l == r )
{
hjt[now].val = ++tot; //初始为1,2,3,4...n
return;
}
int m = ( l + r ) >> 1;
build(l,m,hjt[now].l);
build(m+1,r,hjt[now].r);
}
void modify(int l,int r,int ver,int &now,int loc,int x) //当前区间为[l,r],将版本为ver的loc位置改为x,新版本为now
{
now = ++cnt;
hjt[now] = hjt[ver];
if( l == r )
{
hjt[now].val = x;
return;
}
int m = ( l + r ) >> 1;
if( loc <= m ) modify(l,m,hjt[ver].l,hjt[now].l,loc,x);
else modify(m+1,r,hjt[ver].r,hjt[now].r,loc,x);
}
int query(int l,int r,int now,int loc) //查询某个版本第loc位置的值,当前节点编号为now,区间为[l,r]
{
if( l == r ) return hjt[now].val;
int m = ( l + r ) >> 1;
if( loc <= m ) return query(l,m,hjt[now].l,loc);
else return query(m+1,r,hjt[now].r,loc);
}
int find(int ver,int p) //在版本ver下,查询p的根节点
{
int t = query(1,n,rootfa[ver],p);
if( p == t ) return t;
return find(ver,t);
}
void merge(int ver,int x,int y) //合并版本ver的并查集下x与y所在的集合
{
int fx = find(ver-1,x); //注意当前版本啥也没有,所以找版本-1的值
int fy = find(ver-1,y);
if( fx == fy ) //在同一集合就啥也不用干
{
rootfa[ver] = rootfa[ver-1];
rootdep[ver] = rootdep[ver-1];
return;
}
int siz1 = query(1,n,rootdep[ver-1],fx);
int siz2 = query(1,n,rootdep[ver-1],fy); //查询ver-1版本的深度
if( siz1 < siz2 ) //将x所在的集合合并到y上
{
modify(1,n,rootfa[ver-1],rootfa[ver],fx,fy); //将ver的版本的fx位置的值改为fy
rootdep[ver] = rootdep[ver-1]; //小的并上来不影响深度
}else if( siz2 < siz1 )
{
modify(1,n,rootfa[ver-1],rootfa[ver],fy,fx);
rootdep[ver] = rootdep[ver-1];
}else
{
modify(1,n,rootfa[ver-1],rootfa[ver],fy,fx); //大小相等就随便,这里把y连到x上
modify(1,n,rootdep[ver-1],rootdep[ver],fx,siz1+1); //把x的集合深度加1
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int m;
cin >> n >> m;
build(1,n,rootfa[0]);
for (int i = 1; i <= m; i++)
{
int op;
cin >> op;
if( op == 1 )
{
int x,y;
cin >> x >> y;
merge(i,x,y);
}else if( op == 2 )
{
int ver;
cin >> ver;
rootfa[i] = rootfa[ver]; //返回版本ver,直接复制
rootdep[i] = rootdep[ver];
}else
{
int x,y;
cin >> x >> y;
rootfa[i] = rootfa[i-1]; //并查集没有变,直接复制上个版本
rootdep[i] = rootdep[i-1];
int fx = find(i,x);
int fy = find(i,y);
if( fx == fy ) cout << 1 << '\n';
else cout << 0 << '\n';
}
}
return 0;
}
可持久化并查集
最新推荐文章于 2022-07-07 08:17:33 发布