【笔记】莫队

普通莫队

暴力。把询问按照左端点所在块为第一关键字,右端点升序排序。

暴力修改和询问。

时间复杂度

对于左指针,每一次在块内每一次最多移动 \(B\),在块与块之间最多移动 \(2B\)

时间复杂度为 \(O(Bm)\)

对于右指针,每一个块都要移动 \(n\) 次,时间复杂度为 \(O(\displaystyle \frac{n^2}{B})\)

加起来为 \(O(Bm+\displaystyle \frac{n^2}{B})\)

\(B\) 增大时一个增大一个减小。粗略估计当 \(Bm=\displaystyle \frac{n^2}{B}\) 时,即 \(B=\displaystyle\sqrt\frac{n^2}{m}\) 时取最小值(当然大部分时候 \(n\)\(m\) 的数量级相等,可以取 \(B=\sqrt n\)。)

时间复杂度为 \(O(\displaystyle\sqrt\frac{n^2}{m}\cdot m)=O(\displaystyle\sqrt{\frac{n^2}{m}m^2})=O(n\sqrt m)\)

奇偶优化

对于每一次右指针的移动,我们可以给它在奇数块的时候按升序排序,偶数块的时候按降序排序,这样可以大大优化来回跑的时间。

普通莫队 + 分块

对于某些题,明显每一个块内需要用一种数据结构来维护查询。

首先你肯定第一会想到树状数组或线段树。

想一想,它每一次修改(即移动指针)和查询都是 \(O(\log n)\) 的。

总体时间复杂度为 \(O(n\sqrt m\log n)\)。常数够小,这个时间复杂度是可以被接受的。

但是有没有更快的时间复杂度的呢?

看见一个一个块,我们一定会想到分块(实在没想到也没有关系)。每一次修改其实是 \(O(1)\) 的,查询才是 \(O(\sqrt n)\) 的。

所以使用分块时,时间复杂度是 \(O(n\sqrt m+m\sqrt n)\)。更能接受!

普通莫队 + bitset

实在不能用分块的怎么办呢?我们可以使用 bitset。此时时间复杂度为 \(O(n\sqrt m+\displaystyle\frac{nm}{\omega})\)

带修莫队

和上面基本相同,只是排序方式发生了改变:左端点所在的块的编号为第一关键字,右端点所在的块的编号为第二关键字,时间升序为第三关键字即可。

设有 \(m\) 次查询, \(t\) 次修改。

对于左指针:\(O(mB)\)

对于右指针,自己移动为:\(O(mB)\),左指针变化时为 \(O(\displaystyle \frac {n^2}B)\)

对于时间数组,当端点所在块不同时就要变化,时间复杂度为:\(O(\displaystyle \frac {n^2}{B^2}t)\)

时间复杂度

因为 \(O(\displaystyle \frac {n^2}B) < O(\displaystyle \frac {n^2}{B^2}m)\),所以只用当 \(mB=\displaystyle \frac {n^2}{B^2}t\) 时便可最优。

解得 \(B=\displaystyle \frac {n^{\frac23}t^{\frac13}}{m^{\frac13}}\)。(当然可以简记为 \(B=n^{\frac23}\))。

时间复杂度为 \(O(n^{\frac23} m^{\frac23} t^{\frac13})\approx O(n^{\frac23}m) \approx O(n^{\frac53})\)

回滚莫队

对于不能有删除的莫队(比如说求 \(\max\)),我们可以这么来做。

  • 对于 \(l\)\(r\) 在同一块中,我们可以直接暴力。复杂度:\(O(\sqrt n)\)
  • 对于 \(l\)\(r\) 不在同一个块。我们可以升序扫 \(r\),然后每一次 \(l\)\(l\) 块中的最后一个元素出发暴力扫一遍,再撤销。左指针仍然每次只便利了 \(O(\sqrt n)\) 次。

假设 \(belong_i\) 表示 \(i\) 所在的块的编号。

所以时间复杂度仍为 \(O(n\sqrt{ {m}_{ belong_l=belong_r}}+{m_{ belong_l\neq belong_r}}\sqrt n) \approx O(n\sqrt m+m\sqrt n)\)

树上莫队

拍扁为进去一次,出去一次的类似于欧拉序的东西即可。

实战分析

posted @ 2025-01-19 09:28  GuTongXing  阅读(25)  评论(0)    收藏  举报