并查集进阶

合集下载
相关主题
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
出我们的按秩合并。 • 这题题解说: • 维护一个按秩合并的并查集,给连边操作加时间戳,查询的时候暴力求路径上时间
戳最大值
No.3
扩展域,边带权



\
x
y
x
我可线队
们持段列
俯久树进 身留区出 欣下间图 赏的修上
迹改的
象求方
出向


边带权、扩展域简要思想
• 边带权,顾名思义,就是给并查集连边的时候,给每条边赋上 一个边权,在合并的时候用边权修改下节点的权值,然后用点 权来推导某些性质。
• 最后,我想强调的是,学习信息竞赛是一个快乐和痛苦的过程,一切都要依靠自己的意 志。如果在机房里颓废了,那比在教室里睡觉还可怕。因此,我们一定要有极强的自律 能力,在算法的海洋中直面风雨,渐行渐远,学海无涯苦作舟。
如果标算太难请坚定信念 以那暴力模拟向正解吊唁
不如回头再看一眼题面 蒟蒻的蜕变 神犇出现 终将与Au擦肩
路径压缩
主体部分
03 扩展域或边带权,或两者兼而
有之
开胃菜
02 按秩合并
学习心得
04 如何学习一个算Hale Waihona Puke Baidu¿
要求
• 避免出错 • 讲清算法 • 结合例题 • 结合代码 • 有所拓展 • 实际应用 • 学习心得
提示
• 洛 咕 日 爆id87 • 如果有意见可以学生端举手,我会记下来,下课后统一回复 • 这边强烈建议听课认真点) • 留点情啊
• 其实按秩合并并没有很大的实际用处,也没有多少题去考它。但是如果把它和路径压缩放 在一起优化的话,你就能跑出理论上的魔鬼复杂度O(n·α(depth)),这玩意绝对炸不了。
• 顺便说一句,按秩合并应该是玄学复杂度,理论上大约介于不加优化和路径压缩之间,所 以有一定的风险(但谁来卡这么一个又奇怪又没人用的算法呀)
No.2
开胃菜




你在OJ上提交了千百遍 却依然不能卡进那时限 双手敲尽代码也敲尽岁月 只有我一人 写的题解 凋零在OJ里面
开胃菜:按秩合并
• 路径压缩想必是各个巨佬们都会的优化吧,然而它在带来高效的同时,有一个小小的缺憾, 那就是他没有严格记录输入中各个结点的合并关系,这既是它的优点,又是它的缺点。
思考题:U39061 send题
• 这道图论背景的题,成功把我引上了爆搜的道路,而没有往并查集方向想。
• 我们把每个节点拆成两个节点,虽然这两个节点都代表同一城市,但使用 的时候略有不同,这里记作x1,x2。思考题:这是运用什么思想?
• 这题只要维护两个城市之间路径长度的奇偶性就好了。如果读入了一条奇 数边x-y,我们把x1-y2和x2-y1分别连接。如果读入了一条偶数边x-y,我 们把x1-x2和y1-y2分别连接。
随堂检测&总结:
• 这节课我们学习了并查集。 • 并查集是一种维护节点之间的同属集合性质的数据结构。 • 并查集的最常用优化是 路径压缩 ,它的基本思想是 让一个节点的所有子孙变成它的直接儿子。 • 按秩合并的思想是让合并的两个集合中较浅的合并到较深的上。 • 并查集的两种进阶是 扩展域和边带权。 • 扩展域的关键是拆节点 ,边带权的关键是设点权,边权,写好转移。
• 另外,信息学竞赛的一个最大不同点就是,它很大程度上是没有也不需要辅导老师的。 一是因为水平越高的比赛和算法,理解并能教授的老师就越稀有;二是前辈们有一个写 博客的优良传统,还有很多值得一读的算法书,我们大可以利用这些既有资源来自学; 三是算法和竞赛是在不断前进和扩充的,近几年新出现的算法层出不穷,这是一个动态 的过程,我们需要自己跟上时代潮流,不能过多依赖别人。
口答题:P1551 亲戚(并查集模板)
• #include<bits/stdc++.h>

______②______for (i=1;i<=n;i++) a[i]=i;
• using namespace std;
• int a[5001],b,c,i,n,m,k;
• int f1(int x)
•{
• 这样,如果一个点x和另一个点y之间有奇数边,它就会连接到y2上,而我 们统计的是1系列的节点。要想让这条边影响结果,只能在y2和z1之间再 有一条边——根据上文性质,这也是条奇数边。因此这样连出来的边就能 保证1系列之间的互相连边都是偶数边。然后并查集跑下连通性就好了。
是不是有这种感觉:
No.4

______①______
•}
• void f2(int x,int y)
•{




if (a[x]!=x) a[x]=f1(a[x]); •
return a[x];




for (i=1;i<=m;i++) {
scanf("%d%d",&b,&c); f2(b,c); } for (i=1;i<=k;i++) { scanf("%d%d",&b,&c); b=f1(b);

a[f1(x)]=f1(y);

c=f1(c);
•}
• int main()
•{

scanf("%d%d%d",&n,&m,&k);

i f ( b ==c) p r i n tf("%s \n","Ye s"); e l s e
printf("%s\n","No");

}

return 0;
•}
• 具体实现是,我们用另一个数组存下每个节点的点权,在合并 和路径压缩的时候用边权和另一个节点的点权更新该点点权。
• 而扩展域的基本思想就是,把一个节点拆成k个子节点,每个子 节点存原节点的一个性质,分别存储互不干扰,同时把总数组 开大k倍,然后实现一些关系错综复杂的操作。
e.g.P1196银河英雄传说
• 它既在CCF系列赛制中被经常单独考察,又能融入其他的算法(比如 Kruskal),提高他们的效率,因此用途广泛,涵盖从普及到NOI+的大部 分比赛。
• 并查集的核心代码:
• void find(int x) { if (f[x]==x) return x;else return find(f[x]);} • 另外,使用并查集之前一定要把数组的初值f[i]归为i!
• 如题意简述,我们抽象出30000个队列,对每个节点,我们开个数组h记录该节点和该节点所在 队列队首的距离(也就是设边权为1时的点权)。对于合并操作x->y,我们让h[x]加上y列中的节 点数,这就需要我们再开一个数组b来存每个节点所在列中的节点数,当然这个数组b在合并的 时候也要加和。对于路径压缩,我们让h[x]加上h[f[x]](当前的父节点!),然后把它的父亲更 新。因为这个递归是从树根西向外依次处理的,故不会有问题。
• 我的爸爸的爸爸不是我的爸爸!
• 如果题目里硬要我们维护这个关系,那路径压缩就是一个废柴,我们就得请出另一个优化: 按秩合并。
• 按秩合并是基于一个很容易想到的贪心优化:在我们把两个集合(其实是两棵树)合并的 时候,让小(矮)的那棵树合并到大(高)的那棵树上,叫他爸爸,这样我们每次得到的集 合树都是深度最小的,寻找一个节点的最大复杂度log2(depth)也就越小。
• 有30000条队列,总共n次操作,分M操作与C操作,M操作是把当前第i艘飞船所在的整条队列 移到第j艘飞船艘所在的队列的最后面,C操作则是询问第i艘飞船与第j艘飞船是否在同一条队列, 如果在则输出第i艘飞船与第j艘飞船之间的飞船数,不包括i和j,题目不保证j在i后面,否则就输 出-1。
• 这个题目虽然是维护集合的合并,但元素是有序排列的,看起来又不符合集合的性质。这时候就 需要用到边带权了。
键盘微凉 鼠标微凉 指尖流淌 代码千行 凸包周长 直径多长 一进考场 全都忘光
No.1
前置知识
并查集基础,路径压缩
前置知识:并查集
• 并查集是一个维护一些元素的集合归属的高级数据结构,具有时空双高效 的特点。
• 并查集的基本思想是将给出的同属关系定向后单向合并,用一个数组维护 各个元素所属集合的代表元素。
• 代码
• 双倍经验口答题
e.g.P2024食物链
• 有三种动物,他们互相捕食,构成环状结构。现在有n个动物,有m个命题,表示其中两个动物是同种动物 或捕食与天敌的关系。现在要输出这些命题中假命题的个数。1≤n≤5*104,1≤m≤105。
• 这题有一些很有趣的性质。首先,动物只有三种,只有5*104个动物。5*104*3=1.5*105,不会炸。其次, 三种动物之间构成了一个环,这说明如果A吃B,B吃C,那么C吃A(知道两个就能推出第三个关系),这 使得这题的推导十分方便。然而,我们不可能用一个位置同时存下三种关系(同类,捕食,天敌),这就要 用到扩展域思想。
前置知识:路径压缩
• 上面那个代码是并查集最基本的写法,时间复杂度是—— • 活学活用:CSP2019提高第一轮 27题 • (一道题难倒英雄好汉啊) • 如果你觉得这个代码很慢,那是因为这个代码就是很慢。 • 所以我们需要一些逆天优化,这就是路径压缩。 • 写成代码就是这样: • void find(int x) { if (f[x]==x) return x;else return f[x]=find(f[x]);} • 也就只改了一个小地方嘛,但是小小的细节就能带来大大的优化。 • 它的复杂度变成了O(n·log2n)
竞赛心得



&



这是来自一个古典OIer的教诲
• 任何一个算法都不是孤立的,像前面提到过kruskal要用到并查集,dijkstra也要用到堆 和线段树优化,因此,各个算法之间都是互相联系,相辅相成。
• 学会触类旁通,举一反三,是学习算法的一个重要能力。我可以教你并查集的写法,但 我不可能教你并查集的用法。算法的使用,涉及到建模和数学知识,是很难被教授的。 这种能力基于大量的刷题和平时的拓展,还是得靠自己的积累。
• 另外,这题也可以使用边带权并查集,请大家思考一下,如何设置边带权并查集的边权。
• 设点权、边权为0代表同类,1代表捕食,2代表天敌。合并a和b的时候,让点权num[f[a]]=(num[b]num[a]+side[x]+3)%3(边权),路径压缩的时候,树根的点权等于子节点所有点权之和%3(一定要%3! 不然点权就不在012之间了)(略)
• 我们把每个动物分裂成3个节点,分别保存同类的集合,捕食对象的集合,和天敌的集合。如果两个动物是 同类,那就把他们同类、捕食对象和天敌的集合分别合并。如果A吃B,根据上文的环状结构,我们能推出 应该把A的捕食对象和B的同类,B的捕食对象和A的天敌,B的天敌和A的同类合并,然后就和普通并查集 一模一样了。

f[Y]=X;

large[X]=max(large[X],large[Y]+
1);

}

else

{

f[X]=Y;

large[Y]=max(large[Y],large[X]+
1);

}
• }//large数组初始化为1
拓展题 BZOJ4668 冷战
• 给定 n 个点的图。动态的往图中加边,并且询问某两个点最早什么时候联通,强制在线。 • 由于这题要用到复杂的在线图状结构,故不能用路径压缩随便简化,所以我们就可以拿
我在OI中辗转了千百天 却不让我看AK最后一眼 我用空间换回超限的时间 随重新编译 测完样例 才发现漏洞满篇
完结撒花
屏幕在深夜微微发亮,我心在考场 ——《膜你抄》
秀下代码
• void find(int x)
•{

if (f[x]==x) return x;

return find(f[x]);
•}
• void hebing(int x,int y)
•{

int X=find(x),Y=find(y);

if (large[X]>large[Y])

{
tarjan陪伴强联通分量 生成树完成后思路才闪光 欧拉跑过的七桥古塘 让你 心驰神往
织平思屏
成面想幕
忧的在在 伤向那深 的量虚夜 网交树微
错路微
生径发
长上亮


并查集进阶
13th May
NOIP2020
SPFA 01


颜背 枝
目 录 页 洋
溢 脸 庞
包 装 下 了 忧
告 诉 我 前 途
剪 去 我 们 的
伤 在 疯C o n t e n t s
何狂

前置知识
01 并查集思想和基础实现,
相关文档
最新文档