可持久化并查集【主席树】【启发式合并】

>Link

luogu P3402


>Description

在这里插入图片描述


>解题思路

直接暴力空间会爆。
我们发现一次合并只会修改一个节点的信息。可持久化结构我们会想到主席树,用线段树换数组获取总体更少的空间,那我们就用主席树来储存某个时刻某个节点的父亲
然后询问就直接查询两个点的祖先是否一样即可

合并不压缩路径(据说会mle和tle),为了不tle,就用到启发式合并,集合深度小的并到大的(因为查询祖先的时候与集合深度有关)


>代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 200010
using namespace std;

int n, m, root[N], fa[N * 20], ls[N * 20], rs[N * 20], dep[N * 20], tot;

void build (int &now, int l, int r)
{
	now = ++tot;
	if (l == r)
	{
		fa[now] = l;
		dep[now] = 1;
		return;
	}
	int mid = (l + r) / 2;
	build (ls[now], l, mid);
	build (rs[now], mid + 1, r);
}
void update (int &now, int pre, int l, int r, int pos, int f)
{
	now = ++tot;
	if (l == r)
	{
		fa[now] = f;
		dep[now] = dep[pre];
		return;
	}
	int mid = (l + r) / 2;
	ls[now] = ls[pre], rs[now] = rs[pre];
	if (pos <= mid) update (ls[now], ls[pre], l, mid, pos, f);
	else update (rs[now], rs[pre], mid + 1, r, pos, f);
}
void add (int now, int l, int r, int pos)
{
	if (l == r)
	{
		dep[now]++;
		return;
	}
	int mid = (l + r) / 2;
	if (pos <= mid) add (ls[now], l, mid, pos);
	else add (rs[now], mid + 1, r, pos);
}
int query (int now, int l, int r, int pos)
{
	if (l == r) return now;
	int mid = (l + r) / 2;
	if (pos <= mid) return query (ls[now], l, mid, pos);
	return query (rs[now], mid + 1, r, pos);
}
int findfa (int rt, int x)
{
	int f = query (rt, 1, n, x);
	if (x == fa[f]) return f;
	return findfa (rt, fa[f]);
}

int main()
{
	int op, x, y;
	scanf ("%d%d", &n, &m);
	build (root[0], 1, n);
	for (int i = 1; i <= m; i++)
	{
		scanf ("%d%d", &op, &x);
		if (op == 1)
		{
			root[i] = root[i - 1];
			scanf ("%d", &y);
			x = findfa (root[i], x);
			y = findfa (root[i], y);
			if (fa[x] == fa[y]) continue;
			if (dep[x] > dep[y]) swap (x, y);
			update (root[i], root[i - 1], 1, n, fa[x], fa[y]);
			if (dep[x] == dep[y]) add (root[i], 1, n, fa[y]);
		}
		else if (op == 2) root[i] = root[x];
		else
		{
			root[i] = root[i - 1];
			scanf ("%d", &y);
			x = findfa (root[i], x);
			y = findfa (root[i], y);
			if (fa[x] == fa[y]) printf ("1\n");
			else printf ("0\n");
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值