P3402 【模板】可持久化并查集(可持久化并查集模板)

喜夏-厌秋 提交于 2020-01-25 14:00:49

题目链接:模板题

在这里插入图片描述


普通的并查集是通过 fafa 数组来实现的(或者说是 pp 数组)

可持久化并查集,就是用主席树来维护并查集的 fafa 数组(因为主席树可以记录过去的版本),使之能回退到过去的版本,这里的版本指的是某个操作之前的 fafa 数组

可持久化并查集的合并部分使用的不是路径压缩来优化,而是使用启发式合并:两棵树合并时,将树深小的合并到树深大的,这样树的高度增长比较慢。
若合并两棵树,新的树的高度会增长,那么树的sizesize 至少扩大两倍,因此树高至多增长 logn\log n 次,每次增长的值为 1。

主席树部分:

先建一棵初始的主席树,令每个下标 iifa[i]=ifa[i] = i

void build(int &rt,int l,int r) {
	rt = ++sz;
	if(l == r) {
		dep[rt] = 1;
		fa[rt] = l;
		return ;
	}
	int mid = l + r >> 1;
	build(lson[rt],l,mid);
	build(rson[rt],mid + 1,r);
}

更新:更新是单点更新,套用主席树的模板即可,因为 dep 和 fa 只关心叶子结点的值,其他节点的值不用继承

void upd(int p,int v,int &rt,int l,int r) {
	sz++;
	lson[sz] = lson[rt];
	rson[sz] = rson[rt];
	rt = sz;
	if(l == r) {
		dep[sz] = dep[rt];
		fa[rt] = v;
		return ;
	}
	int mid = l + r >> 1;
	if(p <= mid) upd(p,v,lson[rt],l,mid);
	else upd(p,v,rson[rt],mid + 1,r);
}

查询部分,查询某个结点的父节点,注意不是直接返回并查集的父节点,而是返回下标,因为后面还要比较深度,返回下标更方便

int qry(int p,int rt,int l,int r) {
	if(l == r) return rt;
	int mid = l + r >> 1;
	if(p <= mid) return qry(p,lson[rt],l,mid);
	else return qry(p,rson[rt],mid + 1,r);
}

并查集部分:
查找根节点:因为启发式合并下树高最多log,直接暴力查找,复杂度为两个log

int find(int x,int rt) {
	int fx = qry(x,rt,1,n);
	if(fa[fx] == x) return fx;
	return find(fa[fx],rt);
}

修改深度:当合并时新的树的深度需要修改时,最多 + 1,因此单点修改 + 1即可

void add(int p,int rt,int l,int r) {
	if(l == r) {
		dep[rt]++;
		return ;
	}
	int mid = l + r >> 1;
	if(p <= mid) add(p,lson[rt],l,mid);
	else add(p,rson[rt],mid + 1,r);
}

模板题代码:

#include<iostream>
#include<stdio.h>
using namespace std;
const int maxn = 2e5 + 10;
int n,m;
//用主席树来维护并查集的fa数组,还需要维护一个dep数组以便启发式合并 
int root[maxn],lson[maxn * 30],rson[maxn * 30],dep[maxn * 30],fa[maxn * 30],sz;
int read() {
    int s=0,m=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')m=1;ch=getchar();}
    while(ch>='0'&&ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
    return m?-s:s;
}
void build(int &rt,int l,int r) {
	rt = ++sz;
	if(l == r) {
		dep[rt] = 1;
		fa[rt] = l;
		return ;
	}
	int mid = l + r >> 1;
	build(lson[rt],l,mid);
	build(rson[rt],mid + 1,r);
}
void upd(int p,int v,int &rt,int l,int r) {
	sz++;
	lson[sz] = lson[rt];
	rson[sz] = rson[rt];
	rt = sz;
	if(l == r) {
		dep[sz] = dep[rt];
		fa[rt] = v;
		return ;
	}
	int mid = l + r >> 1;
	if(p <= mid) upd(p,v,lson[rt],l,mid);
	else upd(p,v,rson[rt],mid + 1,r);
}
int qry(int p,int rt,int l,int r) {
	if(l == r) return rt;
	int mid = l + r >> 1;
	if(p <= mid) return qry(p,lson[rt],l,mid);
	else return qry(p,rson[rt],mid + 1,r);
}
int find(int x,int rt) {
	int fx = qry(x,rt,1,n);
	if(fa[fx] == x) return fx;
	return find(fa[fx],rt);
}
void add(int p,int rt,int l,int r) {
	if(l == r) {
		dep[rt]++;
		return ;
	}
	int mid = l + r >> 1;
	if(p <= mid) add(p,lson[rt],l,mid);
	else add(p,rson[rt],mid + 1,r);
}
int main() {
	n = read(); m = read();
	build(root[0],1,n);
	for(int i = 1; i <= m; i++) {
		int op,x,y;
		root[i] = root[i - 1];
		op = read();
		if(op == 1) {
			x = read(); y = read();
			int fx = find(x,root[i]);
			int fy = find(y,root[i]);
			if(fa[fx] == fa[fy]) continue;
			if(dep[fx] > dep[fy]) swap(fx,fy);
			upd(fa[fx],fa[fy],root[i],1,n);
			if(dep[fx] + 1 > dep[fy]) add(fa[fy],root[i],1,n);
		} else if(op == 2) {
			x = read();
			root[i] = root[x];
		} else if(op == 3) {
			x = read(); y = read();
			int fx = find(x,root[i]);
			int fy = find(y,root[i]);
			if(fa[fx] != fa[fy]) puts("0");
			else puts("1");
		}
	}
	return 0;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!