并查集——operator_

文章介绍了并查集这种数据结构的基本概念,包括常规并查集的操作和优化方法,如路径压缩和按高度或子树合并。还讨论了种类并查集的应用,如处理朋友和敌人关系的问题,以及带权并查集的概念,强调了在实际问题中如何维护额外信息。
摘要由CSDN通过智能技术生成

1.常规并查集

并查集是一种支持独立数据间合并与查询是否同组的数据结构。

它的思路很简单:把每一组都看作以某点为首的一棵树,记录每个点的父亲节点,那么就可以不断向上找到根节点,也就代表了这个点的组别。

为了突出标记根节点,一般让根节点与自己 自环

合并 就是将其中一棵树整体作为子树接在另一个点下(无所谓是哪个点,但为了减低复杂度,会选择根节点)。

查询 就是判断两个点是否同属于一组(即有相同的根)。

int f[];
void init(int n) {for(int i=1;i<=n;i++) f[i]=i;}
int find(int x) {return (x==f[x]?x:find(f[x]));}
void merge(int x,int y) {f[find(x)]=find(y);}

2.优化

能看出常规的并查集的复杂度是不稳定的,因为 f i n d find find 函数可能会退化成 O ( n ) O(n) O(n)

所以有两种简单的方法:

1.路径压缩

常规的并查集存储的是每个点的父亲,然而我们要这个 f [ ] f[] f[] 数组其实只是为了求根,那何必要存父亲?任意一个祖先都可以保证正确性,所以我们采用记忆化的方式来压缩路径。

很简单,就改一句话 return (x==f[x]?x:find(f[x])); → \rightarrow return (x==f[x]?x:f[x]=find(f[x]));

2.按高度或子树合并

参考平衡树,尽量让集合的深度低一点,所以可以把浅的数放到深的树下,按子树大小同理。

虽然也很好用,但要多开几个数组,代码要改的地方比较多,平常用路径压缩就足够了。

3.种类并查集

P1892 [BOI2003]团伙 为例。

发现如果只有朋友就是裸的并查集,但有敌人这个东西。理解一下题意,发现一个人的所有敌人都是朋友,所以想到给他们也开一个并查集,进而想到先给每个人都分配一个不存在的敌人,设其为 i + n i+n i+n ,那么每个敌对关系就转化为了朋友关系。

#include<bits/stdc++.h>
using namespace std;
int n,m,p,q,ans;
char op;
int f[20005];
void init(int n) {for(int i=1;i<=n;i++) f[i]=i;}
int find(int x) {return (x==f[x]?x:f[x]=find(f[x]));}
void merge(int x,int y) {f[find(x)]=find(y);}
int main(){
	cin>>n>>m;
	init(2*n);
	for(int i=1;i<=m;i++){
		cin>>op>>p>>q;
		if(op=='F')
			merge(p,q);
		else
			merge(p+n,q),merge(q+n,p);
	}
	for(int i=1;i<=n;i++)
		if(i==find(i)) 
		    ans++;
	cout<<ans<<endl;
	return 0;
}

这是 2 2 2 个种类的并查集,P2024 [NOI2001] 食物链 3 3 3 个。

4.带权并查集

顾名思义,就是每个并查集再维护个什么东西,比较常见的有维护到根的距离,合并时注意点顺序就好了。

这一类因题而异,有点像 d p dp dp

练习题:P1196 [NOI2002] 银河英雄传说

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值