可持久化并查集

/*
可持久化并查集
并查集的本质就是数组,所以可持久化并查集就是可持久化数组
但是并查集的路径压缩不能用了,因为路径压缩会改变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;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值