#206[主席树]可持久化并查集

Description

n个集合 m个操作

操作:

  • 1 a b 合并ab所在集合

  • 2 k 回到第k次操作之后的状态(查询算作操作)

  • 3 a b 询问ab是否属于同一集合,是则输出1否则输出0

5 6
1 1 2
3 1 2
2 0
3 1 2
2 1
3 1 2
  • Sample Input

1
0
1
  • Sample Output

HINT

1≤n≤1051≤m≤2×105

By zky 出题人大神犇

 

Source/Category

可持久化 洛谷P3402  

 

没有路径压缩!(慢死了),直接启发式合并

主席树维护父亲深度

#include <iostream>
#include <cstdio>

using namespace std;
const int MAXN = 100010;

int n, lc[MAXN*60], rc[MAXN*60], val[MAXN*60], dep[MAXN*60], root[MAXN*2], id = 0;

void build(int &rt, int l, int r) { // 建主席树
	rt = ++id;
	if (l==r) {
		val[rt] = 0; dep[rt] = 1; return;
	}
	int m = (l+r) >> 1;
	build(lc[rt], l, m); build(rc[rt], m+1, r);
}
void update(int &rt, int old_rt, int l, int r, int x, int v) { // 更新父亲
	rt = ++id; lc[rt] = lc[old_rt]; rc[rt] = rc[old_rt]; // 新建节点
	if (l==r) {
		val[rt] = v; dep[rt] = dep[old_rt]; return;
	}
	int m = (l+r) >> 1;
	if (x<=m) update(lc[rt], lc[old_rt], l, m, x, v);
	else update(rc[rt], rc[old_rt], m+1, r, x, v);
}
void update_dep(int rt, int l, int r, int x) { // 更新深度
	if (l==r) {
		++dep[rt]; return;
	}
	int m = (l+r) >> 1;
	if (x<=m) update_dep(lc[rt], l, m, x);
	else update_dep(rc[rt], m+1, r, x);
}
int query(int rt, int l, int r, int x) { // 查询(下标)
	if (l==r) return rt; // 返回
	int m = (l+r) >> 1;
	if (x<=m) return query(lc[rt], l, m, x);
	else return query(rc[rt], m+1, r, x);
}
int find(int ed, int x) { // 找祖先
	int fa = val[query(root[ed], 1, n, x)];
	if (fa) return find(ed, fa);
	else return x;
}
int main() {
	int m; scanf("%d%d", &n, &m);
	build(root[0], 1, n);
	for (int i=1; i<=m; ++i) {
		int op, a, b; scanf("%d%d", &op, &a);
		if (op==1) {
			scanf("%d", &b);
			a = find(i-1, a); b = find(i-1, b);
			if (a==b) {
				root[i] = root[i-1]; continue;
			}
			int p = query(root[i-1], 1, n, a); // 合并
			int p2 = query(root[i-1], 1, n, b);
			int s = dep[p];
			int s2 = dep[p2];
			if (s>s2) { // 启发式合并,深度小的合并到深度大的上
				swap(a, b); swap(s, s2);
			}
			update(root[i], root[i-1], 1, n, a, b);
			update_dep(root[i], 1, n, b); // 更新深度
		}
		else if (op==2) root[i] = root[a]; // 回到某个版本
		else {
			scanf("%d", &b); root[i] = root[i-1]; // 查询是否在同一集合
			if (find(i, a)==find(i, b)) printf("1\n");
			else printf("0\n");
		}
	}
	return 0;
}

 

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REAdMe.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REAdMe.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看READme.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值