并查集小记
前言
考前复建一下经典知识点,补充一下带权并查集和扩展域并查集。
分析
普通并查集
先看一个经典问题:
有 \(n\) 个数,\(n\) 个集合,每个数一开始对应一个集合,有两种操作:
1 x y
合并 \(x\) 和 \(y\) 所在的集合。
2 x y
询问 \(x,y\) 是否在同一个集合。
将每个集合赋予树形结构,若干个集合便成为了森林。
考虑维护 \(f_i\) 为 \(i\) 所在集合中 \(i\) 的祖先。
定义一个函数 \(\operatorname{find}(i)\) 代表查询 \(i\) 所在集合的根节点。
对于 \(\operatorname{find}(i)\) 函数的实现,考虑使 \(i\) 在树上不断向上跳即可。
对于 \(1\) 操作,判断 \(\operatorname{find}(x)=\operatorname{find}(y)\) 即可。
对于 \(2\) 操作,显然是合并两棵树,直接将 \(x\) 所在树的根接到 \(y\) 所在树的根上即可。
但是考虑到树的形态最坏是一条链,只是暴力跳的话复杂度会很劣。
引入并查集的两种优化:
-
路径压缩优化:结合上面的分析,对于一棵树,我们只关心这颗树的根节点是谁,而不关心这一结构的具体形态,那么在访问 \(\operatorname{find}(i)\) 时,将 \(i\) 的父亲直接指向树根即可,单次查询均摊复杂度 \(\mathrm{O}(\log n)\)。
-
按秩合并优化:将「秩」定义为树的深度或树的大小,将「秩」记录在根节点上,每次合并将「秩」小的合并到「秩」大的。注意到当 「秩」定义为树的大小时,也被称为「启发式合并」,具体意思是指将「小的结构」合并到「大的结构」中,单次操作均摊复杂度 \(\mathrm{O}(\log n)\)。
-
同时使用两种优化的并查集复杂度为 \(\mathrm{O}(\alpha(n))\)。
集合中单个元素的删除和移动:对于元素 \(x\),创立 \(x\) 的副本 \(x'\),若删除,令 \(f_{x'}=x'\),移动同理,同时访问 \(x\) 时,将 \(x\) 导向 \(x'\)。
带权并查集
考虑维护 \(x\) 到 \(f_x\) 的信息,在路径压缩的时候通过与 \(f_x\) 到其祖先维护的信息进行整合,最终储存 \(x\) 到根节点的信息,被称为带权的并查集。
扩展域并查集
将原来的单点,扩展为若干状态,在这些状态中进行并查集操作。
例题
P1955 先对元素进行离散化,考虑分开处理元素相等和不相等的情况,体现为先通过 \(1\) 操作合并相等元素,通过 \(2\) 操作判定元素是否相等。
P2391 倒序处理询问,显然每个点只会染一次色,考虑用并查集维护连续的一段 \([l,r]\),而这段的根节点是 \(r\)。暴力染色,若遇到已经染色的节点,查询该节点的祖先,跳过去即可。在染色的过程中不断合并已经染色的区间。
UVA1316 对于更加贵的商品,优先放置,且放置在离过期时间更近的地方,与上题类似,维护区间,将 \([l,r]\) 的根节点设为 \(l\),判断 \(1\sim d_i\) 是否有空位即代表 \([1,d_i]\) 尚未被合并成一个大区间。
P1196 带权并查集板子,记录 \(d_x\) 代表 \(x\) 到根节点的距离,对于答案即为 \(d_y-d_x-1\)。
P5937
做法 \(1\):
考虑将给出的条件进行转化。
定义 \(sum_i\) 为 \([1,i]\) 中 \(1\) 个数的奇偶性。
\([i,j]\) 有偶数个 \(1\) 即为 \(sum_{l-1}=sum_r\),有奇数个 \(1\) 即为 \(sum_{l-1}\neq sum_r\)。
定义 \(d_x\) 为 \(x\) 与根节点的奇偶性是否相等,若 \(x,y\) 在同一个集合,此时 \(d_x\ \operatorname{xor}\ d_y\) 的取值即可判定 \(x,y\) 的奇偶性是否相等。
合并集合时,记 \(p,q\) 为 \(x,y\) 所在集合的根节点,不妨令 \(x\) 集合合并到 \(y\) 集合,可以推导出 \(sum_{x}\ \operatorname{xor}\ sum_y=d_x\ \operatorname{xor}\ d_p\ \operatorname{xor} \ d_y\)。
因此 \(d_p=sum_{x}\ \operatorname{xor}\ sum_y\ \operatorname{xor}\ d_x\ \operatorname{xor}\ d_y\)。更新 \(d_p\) 并合并即可。
做法 \(2\):
考虑将 \(i\) 拆为两个节点 \(i_e\) 和 \(i_o\) 代表 \(sum_i=0/1\)。
有偶数个 \(1\) 则代表 \(sum_{l-1}=0\) 与 \(sum_{r}=0\),\(sum_{l-1}=1\) 与 \(sum_{r}=1\) 可以互相推出,合并 \((l-1)_{e}\) 和 \(r_e\),\((l-1)_{o}\) 和 \(r_o\)。
有奇数个 \(1\) 同理合并 \((l-1)_{e}\) 和 \(r_o\),\((l-1)_{o}\) 和 \(r_e\)。
判定进行操作前是否矛盾即可。