浅谈并查集 By cellur925【内含题目食物链、银河英雄传说等】
什么是并查集?
合并!查询!集合!
专业点说?
动态维护若干不重叠的和,支持合并查询的数据结构!(lyd老师说的)
数据结构特点:代表元。即为每个集合选择一个固定的元素,作为整个集合的代表,利用树形结构存储,每个节点都是一个元素,树根是集合的代表元素。(还是lyd老师说的)
两大基本操作
一、合并(merge())
即把两个集合合并到一个的操作。通俗的说,即令其中一个树根为另一个树根的子节点。
void merge(int x,int y)
{
fa[getf(x)]=getf(y);
}
二、查询(getf())
朴素的查询效率太低,这里不再赘述。我们通常用到的高效方法是“路径压缩”(按秩合并由于在大多OI竞赛中并不必要,这里不再介绍)。
关于路径压缩,一图可以见真相。

也就是每次在查询节点在集合内祖先时,不能直接调用f[x],因为他是x的一个非根本祖先。而是调用getf函数通过迭代求解,在迭代的过程中,顺便完成了路径压缩。之后要提到的带权并查集、种类并查集与普通并查集的区别在getf上都有很大体系。可以说这一操作是并查集的核心。
这个操作的均摊复杂度为O(logN)。
int getf(int x)
{
if(x==fa[x]) return x;
return fa[x]=getf(fa[x]);
}
例题1 NOI2015程序自动分析
先要吐槽一句...这个题我自5月18号(大概)至今,已提交近30次,自己写的常数太丑,改了一次又一次,今天终于A了,所以...人活着还是要有梦想的嘛qwq。
是比较裸的并查集了,相等就进行合并操作,但注意本题数据离散程度较大,需要进行离散化。
献上我的卡线代码。
code
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
int t;
int fa[];
int n,cnt,a[],tot,b[];
struct taojun{
int p,q,op;
}node[];
void re(int &x)
{
x=;
char ch=getchar();
bool flag=false;
while(ch<''||ch>'') flag|=(ch=='-'),ch=getchar();
while(ch>=''&&ch<='') x=(x<<)+(x<<)+(ch^),ch=getchar();
x=flag ? -x : x;
}
int getf(int x)
{
if(fa[x]==x) return x;
else return fa[x]=getf(fa[x]);
}
void merge(int k,int h)
{
fa[getf(k)]=getf(h);
}
void discrete()
{
sort(a+,a+cnt+);
tot=unique(a+,a+cnt+)-a-;
}
int query(int x)
{
return lower_bound(a+,a+tot+,x)-a;
}
void analyauto()
{
n=,cnt=,tot=;
re(n);
for(int i=;i<=n;i++)
{
int x=,y=,z=;
re(x),re(y),re(z);
node[i].p=x,node[i].q=y,node[i].op=z;
a[++cnt]=x,a[++cnt]=y;
}
discrete();
for(int i=;i<=n;i++)
{
node[i].p=query(node[i].p);
node[i].q=query(node[i].q);
}
for(int i=;i<=tot;i++) fa[i]=i;
for(int i=;i<=n;i++)
if(node[i].op==) merge(node[i].p,node[i].q);
for(int i=;i<=n;i++)
{
if(node[i].op==)
{
if(getf(node[i].p)==getf(node[i].q))
{
printf("NO\n");
return;
}
}
}
printf("YES\n");
}
int main()
{
re(t);
while(t--)
analyauto();
return ;
}
小结:并查集,在一张无向图中维护节点之间的连通性比较优秀,擅长动态维护许多有传递性的关系。
(还是lyd老师说的)
例题2 NOI2002银河英雄传说
LVYOUYW:神舟是哪年发射的来着?03?杨威利这个名字吼啊,莫非CCF提前一年已经知道了航天员杨利伟的名字?
2333...杨威利是真的存在的日本动漫《银河英雄传说》的主角...
学长您不会被打嘛...
扯淡结束
如果说并查集只是记录了集合的位置关系,那么在一些有权值的题目背景下,仅一个普通并查集维护关系貌似不太够用,我们还另需要一些数组来记录权值,本题就是这样。
我们可以维护一个数组d,用d[x]保存节点x到祖先节点fa[x]之间的边权。在路径压缩的同时,我们可以同时更新信息。
再用一个size[]在每个树根上记录集合大小,就能O(1)地进行查询。
code
#include<cstdio>
#include<algorithm>
#include<cmath> using namespace std; int T;
int fa[],d[],size[];
char ch; int getf(int p)
{
if(p==fa[p]) return p;
int root=getf(fa[p]);
d[p]+=d[fa[p]];
return fa[p]=root;
} void merge(int p,int q)
{
int pp=getf(p);
int qq=getf(q);
fa[pp]=qq;
d[pp]=size[qq];
size[qq]+=size[pp];
} int main()
{
for(int i=;i<=;i++) fa[i]=i,size[i]=;
scanf("%d",&T);
ch=getchar();ch=getchar();
while(T--)
{
int x=,y=;
ch=getchar();
if(ch=='M')
{
scanf("%d%d",&x,&y);
merge(x,y);
}
if(ch=='C')
{
scanf("%d%d",&x,&y);
if(getf(x)!=getf(y))
{
printf("-1\n");
ch=getchar();ch=getchar();
continue;
}
// printf("%d %d\n",d[x],d[y]);
// printf("%d\n",abs(d[x]-d[y]));
printf("%d\n",abs(d[x]-d[y])-);
}
ch=getchar();ch=getchar();
}
return ;
}
例题3 NOI2001 食物链
在我刚学并查集的时候,我用裸并查集骗了20分......
这种并查集,被lyd老师称为‘扩展域’,被学长称作“种类并查集”
当时他说这个偏移量的设计真是 妙 啊!
关于种类并查集,引用一位大神的话:“和基础并查集有很大一部分相同, 多了一个判断2个元素是否属于同一个集团(不是集合, 集合是用来判断2个元素是否能够判断他们属不属于同一个集团:有点绕, 举个例子, 假如知道1和2在不同的集团, 3和4在不同集团,我们就不能判断1和3是否属于一个集团,而集合是用来判断他们是否在同一个集团假如:已知1和2在不同集合,2和3在不同集合, 那么我们就知道1和3在同一个集合);” @Sky_sys,讲的很透彻
因为我在luogu看题解的时候,没有一个人跳出来解释原始的并查集作用是什么,看的我一脸mengbi。这下可是懂了。
于是,我们用一个数组flag[i]记录,表示i的祖先到i的偏移量,每次读入一句话,先看话中涉及的两动物是否在同一集合内,只有在同一集合内,才有可能继续判断;如果不在,我们先把它合并,假设当前这句话是对的。
合并的时候怎么搞?我们还是上一张图。

路径压缩的处理那里自己xjb推一下就好。记得取模!
code
#include<cstdio>
#include<algorithm> using namespace std; int n,k,ans;
int fa[],flag[]; int getf(int x)
{
if(x==fa[x]) return x;
int tmp=fa[x];
fa[x]=getf(fa[x]);
flag[x]=(flag[tmp]+flag[x])%;
return fa[x];
} int main()
{
scanf("%d%d",&n,&k);
for(int i=;i<=n;i++) fa[i]=i;
while(k--)
{
int ops=,a=,b=;
scanf("%d%d%d",&ops,&a,&b);
if(a>n||b>n)
{
ans++;
continue;
}
if(ops==&&a==b)
{
ans++;
continue;
}
int p=getf(a);
int q=getf(b);
if(p==q)
{
if((flag[b]-flag[a]+)%==ops-) continue;
else ans++;
}
else//merge
{
flag[q]=(+ops-+flag[a]-flag[b])%;
fa[q]=p;
}
}
printf("%d",ans);
return ;
}
本文就要结束了,可是对于并查集,本蒟感觉还没有更深入理解qwq。所以过几天再刷几道题吧!qwq
浅谈并查集 By cellur925【内含题目食物链、银河英雄传说等】的更多相关文章
- 浅谈并查集&种类并查集&带权并查集
并查集&种类并查集&带权并查集 前言: 因为是学习记录,所以知识讲解+例题推荐+练习题解都是放在一起的qvq 目录 并查集基础知识 并查集基础题目 种类并查集知识 种类并查集题目 并查 ...
- 浅谈java类集框架和数据结构(2)
继续上一篇浅谈java类集框架和数据结构(1)的内容 上一篇博文简介了java类集框架几大常见集合框架,这一篇博文主要分析一些接口特性以及性能优化. 一:List接口 List是最常见的数据结构了,主 ...
- 谈一谈并查集QAQ(上)
最近几日理了理学过的很多oi知识...发现不知不觉就有很多的知识忘记了... 在聊聊并查集的时候顺便当作巩固吧.... 什么是并查集呢? ( Union Find Set ) 是一种用于处理分离集合的 ...
- 【转载】浅谈大规模k8s集群关于events的那些坑
原文链接:一流铲屎官二流程序员[浅谈大规模k8s集群关于events的那些坑] 背景 随着k8s集群规模的增加,集群内的object数量也与日俱增,那么events的数量也会伴随其大量增加,那么当用户 ...
- 种类并查集(洛谷P2024食物链)
题目描述 动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形.A 吃 B,B 吃 C,C 吃 A. 现有 N 个动物,以 1 - N 编号.每个动物都是 A,B,C 中的一种,但是我 ...
- 浅谈java类集框架和数据结构(1)
在另外一篇博客我简单介绍了java类集框架相关代码和理论. 这一篇博客我主要分析一下各个类集框架的原理以及源码分析. 一:先谈谈LinkedList 这是LinkedList源码的开头,我们能看到几点 ...
- 浅谈字符串哈希 By cellur925
前言 蒟蒻最近在复习字符串算法...但正如之前所说,我OI太菜被关起来了,本蒟蒻只能从最简单的哈希入手了TAT.而别的dalao都在学习AC自动机/后缀数组等高到不知哪里去的算法qwq. 基本思想 映 ...
- 浅谈欧拉函数 By cellur925
1.某神犇Blog 学了三遍的 欧拉函数φ--DEADFISH7 2.我要做一些补充o(* ̄▽ ̄*)o $φ(1)=1$: 公式有两种形式,一种有太多除法,实际可能会慢些.通用 对于任意$n$> ...
- POJ1456:Supermarket(并查集版)
浅谈并查集:https://www.cnblogs.com/AKMer/p/10360090.html 题目传送门:http://poj.org/problem?id=1456 堆作法:https:/ ...
随机推荐
- 洛谷——P1047 校门外的树
P1047 校门外的树 题目描述 某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米.我们可以把马路看成一个数轴,马路的一端在数轴0的位置,另一端在L的位置:数轴上的每个整数点,即0 ...
- 2017多校Round2(hdu6045~hdu6055)
补题进度:10/11 1001(不等式) 根据题意列不等式,解一解就行了 1002(套路) 题意: 给定一个随机产生的1e6*1e6的矩阵和一个1e3*1e3的矩阵,你要回答这个1e3*1e3的小矩阵 ...
- Eclipse打war包方法以及Eclipse移植项目时JDK版本不匹配Project facet Java version 1.7 is not supported
打包时: 在项目上右键选择Export,如图: 然后选择WAR file,如图所示.接着再:其中web projecct为打出来包的名字, Destination,打包后存的位置,点击Browse.. ...
- DNS的工作原理及解析
DNS协议是互联网核心协议之一.不管是上网浏览,还是编程开发,都需要了解一点它的知识. 一.什么是DNS? DNS( Domain Name System)是“域名系统”的英文缩写,是一种组织成域层次 ...
- MySQL学习系列之触发器
触发器简介 触发器作用: 监控某种事件并触发某种动作 触发语法: CREATE TRIGGER trigger_name trigger_event ON tbl_name FOR EACH ROW ...
- Linux内核project导论——网络:Filter(LSF、BPF、eBPF)
概览 LSF(Linux socket filter)起源于BPF(Berkeley Packet Filter).基础从架构一致.但使用更简单.LSF内部的BPF最早是cBPF(classic).后 ...
- Deepin-安装vscode
安装方式有两种: 1.通过命令安装 sudo apt-get install vscode 2.通过deb或rpm包安装 我们是Debian系列的系统,所以用deb包,关于红帽系统,请使用rpm包. ...
- sql server 系统常用函数:聚合函数 数学函数 字符串函数 日期和时间函数和自定义函数
一.系统函数 1.聚合函数 聚合函数常用于GROUP BY子句,在SQL Server 2008提供的所有聚合函数中,除了COUNT函数以外,聚合函数都会忽略空值AVG.COUNT.COUNT_BIG ...
- 【翻译自mos文章】oracle db 中的用户账户被锁--查看oracle用户的尝试次数
參考原文: Users Accounts Getting Locked. (Doc ID 791037.1) 事实上这个文章是为oracle 别的软件产品写的,只是涉及到user 锁定问题.那还是跟d ...
- js可视区域图片懒加载
可视区域图片懒加载 实现原理,页面滚动时获取需要懒加载的图片,判断此图片是否在可视区域内,是则设置图片data-src地址为src地址,加载图片. html下载地址 <!DOCTYPE html ...