学习笔记:莫队
OK 啊,这篇重构了。
0 概念
什么是莫队
可以先去看看分块 这样就很好理解
先丢出一个问题:
- 给出 个区间 求区间众数
这就是蒲公英 在线用分块可以做到 的复杂度
现在我们思考一下
线段树可以做什么?满足区间合并的问题
树状数组可以做什么?满足区间相减的问题
如果这两都做不了?
这下我们的乱搞神器莫队就出来了
它有 的优秀时间复杂度
1 普通莫队
有很多问题 每个重复求很麻烦 不妨按照一个顺序来做
根据分块的想法,分块来做每个问题。
莫队思路:首先,按 左端点所处在的块排序 同一块内的按右端点排序。
然后,直接按照双指针的方法爆搜问题。
这样不会 T 飞吗?
我们来分析一下时间复杂度。
不妨令块长为 。
对于跨过块与块之间的 端点移动,明显,它的复杂度上界为 。
在同一块内,右端点是一直往后的,时间复杂度为 ,加上总共 块,时间复杂度
在同一块内,左端点是会乱走的,假设每次左端点乱走都跑满,那么就会遍历这块一遍。所以时间复杂度是 。
综合一下,根据小学学的基本不等式,我们发现,在 的情况下能跑出最好的期望复杂度:
然后因为因为它能暴力处理出每个点的贡献,所以是非常强大的离线工具。
2 莫队上树
直接将树转成 DFS 序乱搞即可。
如果是子树内的点, dfn 序即可搞定。
如果是一条链上的,那么我们使用 dfs 序。
什么呢?
dfs 序即每次在入点和出点时记录下这个点。
dfs 序有很多。
这棵树的 dfs 序其中之一为:
我们记第一次出现 时为 ,第二次出现为 。
我们发现,一条路径可以通过讨论实现:
- 如果 中, 为 的祖先,那么只需记录下 中只出现一次的数即可。
证明很简单,从 开始就是从已 为根时遍历这棵子树,如果不是路径上的点,要么没有经过,要么经过两次。 - 否则,从 到 中,记录只出现一次的点 再加上 lca 即可。
证明也很简单,从 开始往后回溯,从 的点一定记录了一次,然后再遍历子树 ,这样不会遍历到 ,记得加上。
这样这就是一个普通的莫队问题了。
3 莫队带修
是这样的,莫队根本不能修改。
关于修改,它像标记永久化 seg 一样 ,只能单点修改。
有些时候也可以差分,但比较少,差分后不好查询。但是异或是个例外。
然后跑三维莫队就行了,第一位修改时间,后两维表示区间。
排序优先顺序要和查询顺序相同。
这样乱搞就好了。
理论上,是取块长为 最好,时间复杂度 ,但是大家都说块长取 方便。
4 回滚莫队
你知道的,有些时候,增加一个数可以很容易维护,但是删一个很难维护。
举个例子,求 ,增加一个数直接判断贡献即可。
可是删除一个数,我们发现很麻烦,用一些数据结构会增加一个 这是我们不想看到的。
于是 不删除莫队——回滚莫队出现了。
我们以贡献的角度考虑回滚莫队。
回滚莫队分为几个步骤:
- 先和莫队相同的方法排序
- 如果当前询问属于同一个块(设块长为 )内,直接暴力,与分块思想相同。
- 如果不属于同一块,不妨假定当前扫到块 。我们先设置左端点为 (也就是什么都没有)。
- 然后暴力拓展右端点,根据右端点是排好序的,这一步有保障。
- 对于左端点,每次询问的时候再拓展。然后拆成三部分贡献来分别计算。
- 最后,处理完左端点同一块内的贡献,暴力清空桶。
我们来分析一下时间复杂度。
首先,每次同块暴力时间复杂度为
然后,对于每一块,右端点只会单独往右,时间复杂度为
对于同一块内,每次左端点最多移动 时间,所以时间复杂度为
之后,左端点向左移动,时间复杂度 。
根据小学学的知识,假定 和 同阶,明显,最优块长为 时间复杂度为 。
这样,我们不删莫队就搞定了!
那么不增加呢?
修改一下右端点排序,使右端点一直删,左端点为开头,也删除即可。
理论简单!
5 莫队 + bitset
有些时候我们查询一堆数加/减 能不能搞出来一个 。
我们把存在的数用 串表示,即存在一个位置 ,使得 。
这时候用可以使用 bitset 优化。
每次查询时间复杂度
所以总的时间复杂度
诡异的复杂度 但是可以接受 对于某些题目
6 二次离线莫队
会填的
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)