BZOJ3673 可持久化并查集 by zky <可持久化数组+主席树>

可持久化并查集 by zky

n个集合 m个操作
操作:
1 a b 合并a,b所在集合
2 k 回到第k次操作之后的状态(查询算作操作)
3 a b 询问a,b是否属于同一集合,是则输出1否则输出0

Sample Input
5 6
1 1 2
3 1 2
2 0
3 1 2
2 1
3 1 2
Sample Output
1
0
1
Hint
0 < n,m <= 2*10^4

标签:主席树维护可持久化数组

本题的做法其实和并查集没太大关联。
如果是撤销,那可以用不加路径压缩的并查集完成,但是如果回到某时间,则不太好写。
这时,我们发现并查集这个东西构造其实很简单,只需要一个fa数组就行了。所以我们自然可以想到直接用一个二维数组存储每个时间的fa数组,即用增加的一维表示时间。
但是,n、m是2e4级别,所以肯定会MLE,这里我们就需要用到主席树,把fa数组可持久化。这里需要注意我们不能用路径压缩优化,因为我们需要回到前面的状态,为了让它跑得更快,我们可以用按秩合并优化。
最后附上AC代码:

#include <iostream>
#include <cstdio>
#define MAX_N 20000
using namespace std;
int n, m, cnt, now, root[MAX_N*10+5];
struct node {int fa, dep, ls, rs;} tr[MAX_N*100+5];
void build(int v, int l, int r) {
	if (l == r) {tr[v].fa = l;	return;}
	int mid = l+r>>1;
	tr[v].ls = ++cnt, tr[v].rs = ++cnt;
	build(tr[v].ls, l, mid);
	build(tr[v].rs, mid+1, r);
}
void modifyfa(int v, int o, int s, int t, int pos, int x) {
	tr[v] = tr[o];
	if (s == t)	{tr[v].fa = x;	return;}
	int mid = s+t>>1;
	if (pos <= mid)	modifyfa(tr[v].ls = ++cnt, tr[o].ls, s, mid, pos, x);
	else	modifyfa(tr[v].rs = ++cnt, tr[o].rs, mid+1, t, pos, x);
}
void modifydep(int v, int s, int t, int pos) {
	if (s == t)	{tr[v].dep++;	return;}
	int mid = s+t>>1;
	if (pos <= mid)	modifydep(tr[v].ls, s, mid, pos);
	else	modifydep(tr[v].rs, mid+1, t, pos);
}
int find(int v, int s, int t, int pos) {
	if (s == t)	return v;
	int mid = s+t>>1;
	if (pos <= mid)	return find(tr[v].ls, s, mid, pos);
	else	return find(tr[v].rs, mid+1, t, pos);
}
int getf(int r, int x) {
	int pos = find(r, 1, n, x);
	if (tr[pos].fa != x)	return getf(r, tr[pos].fa);
	return pos;
}
int main() {
	scanf("%d%d", &n, &m);
	cnt = 0, root[0] = ++cnt;
	build(root[0], 1, n);
	for (int now = 1; now <= m; now++) {
		int opt;
		scanf("%d", &opt);
		if (opt == 1) {
			root[now] = root[now-1];
			int a, b;
			scanf("%d%d", &a, &b);
			int posa = getf(root[now], a), posb = getf(root[now], b);
			if (tr[posa].fa == tr[posb].fa)	continue;
			if (tr[posa].dep > tr[posb].dep)	swap(posa, posb);
			root[now] = ++cnt;
			modifyfa(root[now], root[now-1], 1, n, tr[posa].fa, tr[posb].fa);
			if (tr[posa].dep == tr[posb].dep)	modifydep(root[now], 1, n, tr[posb].fa);
		}
		if (opt == 2) {
			int k;
			scanf("%d", &k);
			root[now] = root[k];
		}
		if (opt == 3) {
			root[now] = root[now-1];
			int a, b;
			scanf("%d%d", &a, &b);
			if (tr[getf(root[now], a)].fa == tr[getf(root[now], b)].fa)	printf("1\n");
			else	printf("0\n");
		}
	}
	return 0;
}
posted @ 2017-09-20 15:23  Azrael_Death  阅读(150)  评论(0编辑  收藏  举报