并查集
是一个很简单的数据结构.
但它的实现非常精妙,作用也不可忽视.
定义
- 并查集是一种可以动态维护若干不重叠的集合,并支持合并与查询的数据结构.
操作
一般来说,支持两种:
- Get:查询一个元素属于哪一个集合.
- Merge : 合并两个集合为一个大集合.
对于这两个操作,并查集巧妙的利用 fa[x] f a [ x ] 表示 x x 的父节点这一信息.
对于第一个操作,直接不断递归查找即可.
对于第二个操作,则直接合并两个点的最原始 fa f a 即可,本质上也是第一个查询操作.
按秩合并、路径压缩
本质上是为了提高并查集的查询效率.
对于路径压缩,相信大家都接触过,这里不必多说.
要讲的是按秩合并.
所谓按秩合并,即每次把秩较小的并查集合并到秩较大的并查集.
这里的秩一般定义为树的深度(不进行路径压缩),当定义为树的大小时,也叫启发式合并.
一搬来说,使用启发式合并,可以使总复杂度不超过 O(NlogN) O ( N l o g N ) ,即均摊复杂度将为 O(logN) O ( l o g N ) .
而同时采用路径压缩和按秩合并的并查集,每次 Get G e t 的均摊复杂度可以进一步降到 O(α(N)) O ( α ( N ) ) .
对于 ∀N≤221019279 ∀ N ≤ 2 2 10 19279 ,都有 α(N)≤4 α ( N ) ≤ 4 ,所以 α(N) α ( N ) 几乎可以近似一个常数.
应用
Problem1 P r o b l e m 1 联通快
给定一张 N N 个点,条边的图, Q Q 个询问,每次询问删掉区间中的边后图的联通块个数.
N≤500,M≤10000,Q≤20000 N ≤ 500 , M ≤ 10000 , Q ≤ 20000
Solution
这题需要学会运用合并两个并查集.
我们记录 f[i] f [ i ] 表示前 i i 条边并查集的联通状态.
表示后 i i 条边并查集的联通状态.
合并的两个并查集,即为一次询问的结果.
要合并,我们可以再多建一个并查集 H H ,一开始先把中的并查集全部复制一遍.
然后每次即合并当前的 H(j) H ( j ) 的父亲以及 g[r+1](j) g [ r + 1 ] ( j ) 的父亲.
Problem2 P r o b l e m 2 【NOI2015】程序自动分析
经典的并查集.
先把相同的条件做一遍,然后把不同的判一下即可.
这道题告诉我们,做题不要做傻,不要把问题复杂化.
Problem3 P r o b l e m 3 Supermarket
很容易想到堆的做法。
然而实质上,有更快的做法,我们可以把物品按从大到小排个序,那么一个物品如果在 d d 天过期.
我们就,然后把 find(d) f i n d ( d ) 与 find(d−1) f i n d ( d − 1 ) 合并,累加一下答案即可.
显然这是对的,并且效率极高。
Problem4 P r o b l e m 4 1779. 奇偶游戏【难】
其实和 P2 P 2 是类似的.
只不过把约束条件换成了奇偶性判断.
我们只需新建一个 d[x] d [ x ] 表示 x x 与其父亲的奇偶关系。
假设现在要合并,那就找到其对应祖先 xx,yy x x , y y ,把 xx x x 向 yy y y 连一条边,权值为 dx xor dy xor now d x x o r d y x o r n o w .
dx d x 表示节点 x x 到其祖先这一条路径上的异或和.
表示当前 x,y x , y 的关系.
0 0 表示相同,表示不同。
Problem5 P r o b l e m 5 5798. 2018.08.11【2018提高组】模拟A组 树
给一颗 n n 个结点的树,个节点对,形式为 (a1,b1),(a2,b2),…(am,bm) ( a 1 , b 1 ) , ( a 2 , b 2 ) , … ( a m , b m )
给每一条边定向,使得每一对节点对存在一条从 ai a i 到 bi b i 或从 bi b i 到 ai a i 的路径。
Solution
不得不说这是一道好题。
首先,可以发现,一条路径可以拆为两条路径,对于两条路径上的某一条,对应边都是相同的,只有连向 LCA L C A 的两条边是不一样的。
所以可以类似上题,用并查集维护.
注意,因为往上跳时有可能从一个点跳到同一高度的点,这一种情况很难处理.
所以我们就打两颗并查集,一颗是维护答案的,一颗是原树的并查集,走过一些边就并起来.
Problem6 P r o b l e m 6 5822. 【NOIP提高A组模拟2018.8.16】 量子纠缠
要求你构造一个支持三种操作的字符串集:
- 在字符串集中加入一个字符串
- 查询一个字符串是否出现过
- 纠缠两个字符串 A,B A , B ,即如果字符串中有 A+C A + C ,则要满足有 B+C B + C ,否则加上,并且要一直满足,同理如果有 B+C B + C ,则也要有 A+C A + C
总长度 |S|≤8∗106 | S | ≤ 8 ∗ 10 6
Solution
这道题的做法很神奇.
假设没有三操作,建Trie即可.
考虑三操作,这实质上是把Trie上对应的两个点变为同一个点.
这可以考虑并查集.
我们假设要纠缠两个字符串 A,B A , B ,那么在 Trie T r i e 上找到 A,B A , B 对应的节点,然后可以把其中一个点的 fa f a 设为另一个点.
然后继续往子树去做,有公共点就继续上述操作,否则如果一个点有一条 c c 出边到点,而另外一个点没有这条 c c 出边,我们就把另外一个点的出边连向 d d ,然后不用进行上述操作.
可以看到,这实质上是一个递归合并的过程。
需要注意的是,我们在查询,插入,或者纠缠的时候,都一定是走一条字母边,然后走到其对应的,这样子才能保证最终走到的点一定是涵盖了所有纠缠在一起的字符串。