启发式合并
前言
启发式合并十分得神奇,如果不刻意去想可能几乎想不到这种优化方法,但是在某些时刻看似是暴力的代码套上这几个字再加上证明复杂度,就能缩一圈,但没有模板,代码因题而异,十分灵活。
概念简述
-
对于 (n) 个初始大小为1的可重集,每次选择(被动)两个集合合并时,每次将 (S_1) 与 (S_2) 合并的代价是其中一个集合的大小,而目的时合并成 (1) 个可重集的最小代价。
每次合并,很显然的一种贪心方法是将较小的集合合并到较大的集合,复杂度为 (O(min(S_1,S_2))) ,而最后的时间复杂度的阶也很神奇,是 (O(nlog_2n))。
证明
观察每次合并操作将较小的集合合并到较大的集合,而每个元素只有在较小的集合才会产生贡献,但每次合并操作对于较小的集合都会扩大一倍,而对于刚开始的大小为 (1) 的集合,最多会带此元素作为较小的集合扩大 (lfloorlog_2n floor) 次,而每个元素扩大 (lfloorlog_2n floor) 次后复杂度为 (O(nlog_2n)) ,对于随机数据完全达不到这个上限,我们还是保守的将其估计为此。
例题
(持续更新ing)
-
树上启发式合并模板题。
将问题转换到了树上,其实就是规定了每次操作合并的两个可重集。
在 DFS 遍历到某个节点的时候保留一颗最大的子树的答案,再将其它兄弟子树所有节点后合并到那颗最大的子树的答案中,得到父亲节点的答案。
在一个节点中,每次对于要合并的子树大小最少扩大一倍,而对于某个节点,它在较小的子树中合并产生贡献为包括它的所有子树的合并次数,而这个合并次数不超过 (log_2n) ,因此合并次数也是不超过 (O(nlog_2n)) (对于随机数据十分得松)。