【笔记】启发式合并

来自SharpnessV省选复习计划中的启发式合并


启发式合并用于解决这样一类问题

给定n个集合,每个集合开始只有1个数,你需要支持每次合并两个集合

直接做显然是N2的,但我们可以稍加优化。

每次合并,我们都选择较小的一个集合,将它合并到较大的集合上。

看上去本质上没有改变,但时间复杂度直接由 N2 降低至 Nlog

为什么复杂度是 Nlog 的,因为对于每个元素,每次合并一定是从小的集合合并到大的集合,所以合并后的集合大小一定翻倍,那么一个元素最多移动 log 次,总的时间复杂度为 Nlog

例题1

简述:给定 N 个初始有 1 个元素的栈,每次支持将一个栈合并到另一个栈(按栈的出入顺序)。

直接模拟不难做到N2的复杂度。

观察一下发现这就是启发式合并的模型,考虑用启发式合并解决。

我们用双端队列模拟一个栈,开始队首对应栈顶。

启发式合并时,发现栈 XY得到的栈正好是 YX得到的栈的反序,所以我们对双端队列记录一个op表示队列里的元素是否反转。

代码

例题2

简述:支持连边操作和路径第k小操作,强制在线。

如果不强制在线我们可以将最终的树建出来,然后直接可持久化线段树。

如果强制在线,我们可以考虑动态建树,但由于连边后根会移动,所以我们需要对整棵树重新构建。

但观察一下可以发现,我们合并X,Y两棵树时,有一棵树可以不用改变,所以合并时我们直接暴力重构较小的树,时空复杂度为 O(Nlog2N)

代码

例题3

不难发现对于一条直上直下的单链,有多少点就要划分多少段。

所以划分段数就是子树深度,我们用堆维护这些分段,然后启发式合并即可。

时间复杂度O(Nlog2N)

代码

例题4

简述:支持合并联通块和查询联通块第k小。

用平衡树维护联通块中所以值,然后直接启发式合并即可。

代码

例题5

简述:可持久化并查集

由于可持久化后不便于路径压缩,所以考虑启发式合并。并查集按深度启发式合并,同样可以得到O(NlogN)的时间复杂度。

用可持久化线段树实现可持久化数组,然后时限并查集,时间复杂度O(Nlog2N)

代码


树上启发式合并,简称 DSU on Tree

解决一类静态子树问题,我们按照以下顺序。

  • 计算所有轻儿子贡献,并删除贡献
  • 计算重儿子贡献,保留贡献
  • 将轻儿子贡献合并到重儿子中
  • 回答当前子树询问,回溯

不难发现一个节点会在重儿子的子树中总共计算一次,在每个轻儿子的子树中计算1次,但一个节点到根的作为轻儿子的次数不超过logN,所以时间复杂度为O(NlogN)

例题6

模板题,类似莫队开一个桶记录每种颜色出现次数,然后套用上面的套路即可。

代码

例题7

需要一些技巧的 DSU ,由于要统计所有路径,我们可以类似点分治先递归计算所有子树中路劲的最大值,再计算以当前点为 LCA 的路径。

计算当前点的路径,我们开一个桶记录路径异或值为x的所有路径中,能够达到的最大深度是多少。然后在合并子树时统计答案即可。

我们仍然按照套路合并子树,这样时间复杂度是O(NlogNlogmaxAi)

代码

作者:7KByte

出处:https://www.cnblogs.com/7KByte/p/16143270.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   7KByte  阅读(439)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示