学习笔记:莫队

OK 啊,这篇重构了。

0 概念

什么是莫队
可以先去看看分块 这样就很好理解

先丢出一个问题:

  • 给出 \(m\) 个区间 \(l,r\) 求区间众数

这就是蒲公英 在线用分块可以做到 \(O(n\sqrt n)\) 的复杂度
现在我们思考一下
线段树可以做什么?满足区间合并的问题
树状数组可以做什么?满足区间相减的问题
如果这两都做不了?
这下我们的乱搞神器莫队就出来了
它有 \(O(n\sqrt n)\) 的优秀时间复杂度

1 普通莫队

有很多问题 每个重复求很麻烦 不妨按照一个顺序来做
根据分块的想法,分块来做每个问题。
莫队思路:首先,按 \(l\) 左端点所处在的块排序 同一块内的按右端点排序。
然后,直接按照双指针的方法爆搜问题。
这样不会 T 飞吗?
我们来分析一下时间复杂度。
不妨令块长为 \(S\)
对于跨过块与块之间的 \(l\) 端点移动,明显,它的复杂度上界为 \(O(n)\)
在同一块内,右端点是一直往后的,时间复杂度为 \(O(n)\) ,加上总共 \(\frac{n}{S}\) 块,时间复杂度 \(O(\frac{n^2}{S})\)
在同一块内,左端点是会乱走的,假设每次左端点乱走都跑满,那么就会遍历这块一遍。所以时间复杂度是 \(O(n\times S)\)
综合一下,根据小学学的基本不等式,我们发现,在 \(S=\sqrt n\) 的情况下能跑出最好的期望复杂度:\(O(n^{1.5})\)

然后因为因为它能暴力处理出每个点的贡献,所以是非常强大的离线工具。

2 莫队上树

直接将树转成 DFS 序乱搞即可。
如果是子树内的点, dfn 序即可搞定。
如果是一条链上的,那么我们使用 dfs 序。
什么呢?
image
dfs 序即每次在入点和出点时记录下这个点。
dfs 序有很多。
这棵树的 dfs 序其中之一为:

\[1,2,4,4,5,5,2,3,6,7,7,8,8,6,3,1 \]

我们记第一次出现 \(x\) 时为 \(st_x\) ,第二次出现为 \(ed_x\)
我们发现,一条路径可以通过讨论实现:

  • 如果 \(u\to v\) 中,\(u\)\(v\) 的祖先,那么只需记录下 \(st_v\to st_u\) 中只出现一次的数即可。
    证明很简单,从 \(st_x\) 开始就是从已 \(st_x\) 为根时遍历这棵子树,如果不是路径上的点,要么没有经过,要么经过两次。
  • 否则,从 \(ed_u\)\(st_v\) 中,记录只出现一次的点 再加上 lca 即可。
    证明也很简单,从 \(ed_u\) 开始往后回溯,从 \(u\to lca\) 的点一定记录了一次,然后再遍历子树 \(v\),这样不会遍历到 \(lca\) ,记得加上。

这样这就是一个普通的莫队问题了。

3 莫队带修

是这样的,莫队根本不能修改。
关于修改,它像标记永久化 seg 一样 ,只能单点修改。
有些时候也可以差分,但比较少,差分后不好查询。但是异或是个例外。
然后跑三维莫队就行了,第一位修改时间,后两维表示区间。
排序优先顺序要和查询顺序相同。
这样乱搞就好了。
理论上,是取块长为 \(\frac{2^\frac{1}{3}n^{\frac{2}{3}}t^\frac{1}{3}}{m^\frac{1}{3}}\) 最好,时间复杂度 \(O(n^\frac{5}{3})\),但是大家都说块长取 \(n^\frac{2}{3}\) 方便。

4 回滚莫队

你知道的,有些时候,增加一个数可以很容易维护,但是删一个很难维护。
举个例子,求 \(\max_{l}^r a_i\) ,增加一个数直接判断贡献即可。
可是删除一个数,我们发现很麻烦,用一些数据结构会增加一个 \(\log\) 这是我们不想看到的。

于是 不删除莫队——回滚莫队出现了。
我们以贡献的角度考虑回滚莫队。
回滚莫队分为几个步骤:

  • 先和莫队相同的方法排序
  • 如果当前询问属于同一个块(设块长为 \(S\))内,直接暴力,与分块思想相同。
  • 如果不属于同一块,不妨假定当前扫到块 \(x\) 。我们先设置左端点为 \(R_x+1\)(也就是什么都没有)。
  • 然后暴力拓展右端点,根据右端点是排好序的,这一步有保障。
  • 对于左端点,每次询问的时候再拓展。然后拆成三部分贡献来分别计算。
  • 最后,处理完左端点同一块内的贡献,暴力清空桶。

我们来分析一下时间复杂度。
首先,每次同块暴力时间复杂度为 \(O(S)\)
然后,对于每一块,右端点只会单独往右,时间复杂度为 \(O(\frac{n^2}{S})\)
对于同一块内,每次左端点最多移动 \(O(S)\) 时间,所以时间复杂度为 \(O(mS)\)
之后,左端点向左移动,时间复杂度 \(O(n)\)

根据小学学的知识,假定 \(m\)\(n\) 同阶,明显,最优块长为 \(\sqrt n\) 时间复杂度为 \(O(n^{1.5})\)
这样,我们不删莫队就搞定了!

那么不增加呢?
修改一下右端点排序,使右端点一直删,左端点为开头,也删除即可。

理论简单!

5 莫队 + bitset

有些时候我们查询一堆数加/减 能不能搞出来一个 \(x\)
我们把存在的数用 \(01\) 串表示,即存在一个位置 \(p\) ,使得 \(A_p=A_{p-x}=1\)
这时候用可以使用 bitset 优化。

每次查询时间复杂度 \(O(\frac{n}{128})\)
所以总的时间复杂度 \(O(n\sqrt n+\frac{n^2}{128})\)

诡异的复杂度 但是可以接受 对于某些题目

6 二次离线莫队

会填的

posted @ 2023-09-15 19:57  g1ove  阅读(25)  评论(1编辑  收藏  举报