首页 > 其他分享 >并查集の进阶用法

并查集の进阶用法

时间:2023-04-26 19:45:08浏览次数:62  
标签:元素 进阶 int 查集 用法 fid 集合 所属

普通并查集

我们在处理问题的时候,可能会遇到一些需要维护每个元素所在的集合的问题,而并查集却恰好完美解决了这个问题。

对于普通的并查集,他支持的操作比较少,只有合并和查询,合并是指把两个集合合并成一个,而查询是询问两个元素是否在同一集合内;对于这两种操作,我们可以用一个数组 \(f\) 来存放当前点所属的集合的代表元素编号,例如,\(f[i]=3\) 表示的就是第 \(i\) 个元素属于编号为 \(3\) 的元素所在的集合,那么你可能会问了,用了这个数组,查询是好办了,我们可以直接写一个 find 函数来查询:

inline void fid(int x){return f[x]==x?x:fid(f[x]);}
//如果当前的父节点是自己就返回自己,反之返回代表元素的集合所在的代表元素编号依次直到f[x]==x;

当然我们在使用的时候发现这个 fid 如果一直往后递归会很费时间,所以我们用可以用以下的代码来优化一下:

inline void fid(int x){return f[x]==x?x:f[x]=fid(f[x]);}

这种优化也叫路径压缩,我个人的理解就是加了个记忆化。

那合并呢,咋搞?

假设我们需要把 \(x\) 和 \(y\) 所属的两个集合合并。

法一:直接暴力把所有的点遍历一遍,把所有的属于 \(y\) 所属的集合的 \(f\) 数组给暴力修改成 \(x\) 所属的集合的代表元素编号。复杂度 \(O(n)\)

法二:只修改 \(f[y]\) 的值,让他所属的集合代表元素的编号修改为 \(x\) 所属的集合代表元素的编号,后面在遇到和 \(y\) 原先属于同一集合的元素时再更新。复杂度 \(O(1)\)

很显然第一个会 T 飞,第二个复杂度很不错但是如何来实现呢?

我们之前记录的 \(f\) 数组是有大用的,配合我们的 fid 函数使我们可以轻而易举的完成法二,当你把 \(y\) 所属的集合的代表元素编号改为 \(x\) 所属的集合的代表元素编号,这样我们在后面的时候再查到所属集合为 \(y\) 所属的集合的元素时,我们就会因为 fid 函数一步一步递归更新 \(f\) 数组的值来实现法二操作了。

但是当 \(f[y]=k\) 而 \(f[k]=k\) 的时候怎么办,那样不就没法更新了吗?

所以我们在进行修改的时候改的其实并不是把 \(f[y]=f[x]\),而是 \(f[fid(y)]=f[fid(x)]\),直接从根源合并,这样就可以做到合并操作了。

P3367 【模板】并查集

code:

#include<bits/stdc++.h>
using namespace std;
int n,m,f[100010];
inline int fid(int x)
{
	if(f[x]==x)return x;
	else return f[x]=fid(f[x]);
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	  f[i]=i; 
	for(int i=1;i<=m;i++)
	{
		int op,x,y;
		cin>>op>>x>>y;
		if(op==1)
		{
			int xx=fid(x);
			int yy=fid(y);
			f[xx]=f[yy];
		}
		else if(op==2)
		{
			int xx=fid(x);
			int yy=fid(y);
			if(xx==yy)
			  cout<<"Y"<<endl;
			else cout<<"N"<<endl;
		}
	}
	return 0;
}

扩展域并查集

我也不知道是不是叫这个名字,但是是解决 \(n\) 个点有 \(m\) 对关系,把 \(n\) 个节点放入两个集合里,要求每对存在关系的两个节点不能放在同一个集合这类的问题的一个并查集。

结合一道题目来讲一下具体怎么用:

P1892 [BOI2003]团伙

一个人的朋友的朋友是朋友
一个人的敌人的敌人是朋友

标签:元素,进阶,int,查集,用法,fid,集合,所属
From: https://www.cnblogs.com/Multitree/p/17357072.html

相关文章

  • Vue3 Hooks 的基础用法
    前言Vue3在API设计上引入了类似于ReactHooks的CompositionAPI,可以更方便地实现逻辑的复用和组合。本文将介绍Vue3Hooks的基础用法。基本使用Vue3Hooks中最简单的Hook就是setup。它是一个在组件创建时执行的函数,可以访问组件实例中的属性和方法,同时可以返回一......
  • while循环逻辑控制器+配置元件计数器的用法
    一、在线程组下添加逻辑控制器WhileController二、在逻辑控制器WhileController下添加Sample,BeanShellSampler,三、逻辑控制器WhileController下添加配置元件,计数器四、在线程组下添加监听器,察看结果树:注意while中设置的是${__javaScript("${number}"<"4")},而请求出......
  • Django进阶:事务操作、悲观锁和乐观锁
    Django进阶:事务操作、悲观锁和乐观锁参考网址https://zhuanlan.zhihu.com/p/372957129事务处理(transaction)对于Web应用开发至关重要,它可以维护数据库的完整性,使整个系统更加安全。比如用户A通过网络转账给用户B,数据库里A账户中的钱已经扣掉,而B账户在接收过程中服务器......
  • Android进阶之路 - Java 单元测试
    在此之前,我在单元测试的时候,往往会单独创建一个Demo去进行功能实现,这俩天正好闲下来,所以快速的掌握了一下这个知识点,挺简单的,下面看图说话,看完你就出师了Lookhere~:此文讲的并不高深,扩展也有限,我的目的仅仅是初步且快速的掌握单元测试使用方式,从而提升自己的开发效率~单元......
  • Kotlin进阶指南 - 单元测试
    为了减少一些功能繁琐的测试流程,单元测试是提升开发效率的有效方式之一在早些年的时候我有记录过一篇Android使用单元测试,只不过当时更多的针对Java方面的单元测试;在使用Kotlin后,我发现单元测试有点不同,好像又没什么改变,故此直接记录一篇针对Java、Kotlin都可以使用的......
  • Django4全栈进阶之路19 项目实战(用户管理):user_delete.html用户删除画面设计
    1、模块:<tbody>{%foruserinuser_list%}<tr><td>{{user.username}}</td><td>{{user.email}}</td>......
  • Django4全栈进阶之路18 项目实战:登录模块设计
    1、编写函数视图,判定用户名密码,验证通过进入home主页,不通过返回登录页面deflogin_view(request):ifrequest.method=='POST':username=request.POST.get('username')password=request.POST.get('password')print(username)......
  • python open 用法
    函数语法open(file,mode,buffering,encoding,errors,newline,closefd,opener)参数说明:name:一个包含了你要访问的文件名称的字符串值。mode:mode决定了打开文件的模式:只读,写入,追加等。所有可取值见如下的完全列表。这个参数是非强制的,默认文件访问模式为只读......
  • 对count distinct的用法
    平均活跃天数和月活人数_牛客题霸_牛客网(nowcoder.com)在牛客做这道题时看到了这样的写法。count(distinctuid,date_format(submit_time,"%Y%m%d") 不禁疑惑count里面可以跟两个参数吗。其实不是的,还是只有一个参数。这里面的distinct先起作用.例如:select d......
  • 并查集
    并查集并查集是一种用于管理元素所属集合的数据结构,实现为一个森林,其中每棵树表示一个集合,树中的节点表示对应集合中的元素合并(Union):合并两个元素所属集合(合并对应的树)查询(Find):查询某个元素所属集合(查询对应的树的根节点),这可以用于判断两个元素是否属于同一集合importjava.u......