【笔记】莫队
来自\(\texttt{SharpnessV}\)的省选复习计划中的莫队。
莫队算法是对一类暴力的优化。
如果我们能够在 \(\rm O(1)\) 的时间内由 \([l,r]\) 的答案推出 \([l\pm1,r\pm 1]\) 的答案,就可以考虑使用莫队。
我们对询问排序,首先将序列分为\(\sqrt{N}\)块,左端点在不同块的按左端点大小排序,否则按右端点大小排序。
排序后,我们每次暴力移动左右指针,从上次询问答案推出当前答案。
复杂度分析:对于左端点在同一块的所有询问,每个询问左端点移动不超过 \(\sqrt{N}\),右端点有序,所以总移动不超过 \(N\),总的移动次数就是 \(\rm N\sqrt N\)。(假定序列长度和询问次数同级,下同)
由于一次移动是\(\rm O(1)\)的,所以时间复杂度为 \(\rm O(N\sqrt{N})\)。
一个比较有用的常数优化就是奇偶块排序,左端点在第奇数块的询问右端点递增排序,否则右端点递减排序,这一般能优化一倍以上的常数。
如果莫队常数足够优秀的话,在没有特意构造数据的情况下甚至可以看成一个\(\log\)的。
模板题,开一个桶记录每个颜色出现了多少次。
一下是本质相同的几道题。
莫队算法甚至可以支持修改。
一般莫队,都是维护两个维度表示区间左右端点,我们可以再加入第三个维度,表示当前询问之前有多少次修改。
然后我们将左右端点分块排序,先移动左右端点,再移动时间指针。
如果我们将块大小取到\(\sqrt[3]{NT}\)复杂度最优,其中\(T\)为修改次数,不难证明最劣时间复杂度为\(\rm O(N^{\frac{3}{5}})\)。
本质相同。
莫队甚至可以维护树上问题。
一般莫队,都是解决序列问题,我们可以试着将树上问题转换为序列问题。
如果是子树询问,我们可以通过 DFS序 转换为序列问题。
如果是路径询问,我们可以通过 欧拉序 转换为序列问题。
如果是序列上数颜色则是莫队模板,路径上数颜色我们通过欧拉序转换为序列问题再进行求解。
我们还可以将树上莫队和带修莫队结合得到树上带修莫队,时间复杂度仍然是\(\rm O(N^{\frac{5}{3}})\)。
糖果公园掉紫了,爷青结。
莫队甚至可以不用支持删除操作。
一般莫队,都需要支持\(\rm O(1)\)增/减操作,我们可以试着只用增操作完成。
对于左端点在同一块的询问,右指针单调递增。但是左指针会左右横跳。
可是左端点每次不会跳超过\(\sqrt{N}\),所以我们先将左指针固定到当前块的右端点,对于每个询问,暴力更新左指针。回答完询问后需要将左指针复原到当前块的右端点。
这样做时间复杂度仍然是\(\rm O(N\sqrt{N})\)。
莫队甚至可以和数据结构结合。
一般莫队,都需要支持\(\rm O(1)\)移动端点,而数据结构都是带\(\log\)的,我们可以试着使用一些特殊的数据结构。
我们需要支持单点,和查询区间和。目前最优的方式是树状数组做到\(\rm O(N\log N)\)。
但是对于莫队来说,单点操作多达\(N\sqrt{N}\)次,而询问操作只有\(\rm O(N)\)次,所以我们可以使用值域分块完成,因为值域分块支持\(\rm O(1)\)修改\(\rm O(\sqrt{N})\)查询。
我们还可以将莫队与bitset
结合,因为bitset
支持\(\rm O(1)\)修改,和\(\rm O(\dfrac{N}{64})\)查询,这样总的时间复杂度为\(\rm O(N\sqrt{N}+\dfrac{N^2}{64})\)。
莫队甚至可以解决序列以外的问题。
一般莫队,不论怎么变换都离不开区间查询的本质,但我们还可以灵活运用它。
我们可以通过容斥将问题转换为区间\([1,l]\)和区间\([1,r]\)中颜色相同的点对数。
这不再是我们所熟知的区间\([l,r]\),但是我们仍然把它看作广义区间,并开桶维护。时间复杂度\(\rm O(N\sqrt{N})\)。