莫队算法,通过对询问离线、分块和排序,看似暴力却能巧妙优化复杂度。
概述
莫队算法的用武之地是一类询问可以离线,且可以通过区间 $[l,r]$ 的答案在 $O(1)$ 或 $O(\log n)$ 得到区间 $[l\pm 1,r]$ 或 $[l,r\pm 1]$ 的答案的问题。它通过对询问离线、分块和排序,巧妙地将时间复杂度从 $O(nm)$ 优化到 $O((n+m)\sqrt n)$。
例题
我们以 「国家集训队 2009」小 Z 的袜子 为例。
小 Z 有 $n$ 只袜子,每只袜子都有一个颜色 $c_i$。他有 $m$ 次询问,每次询问给出一个区间 $[l,r]$,求出在这个区间里随机抽 $2$ 只袜子,这 $2$ 只袜子有多大的概率颜色相同。
数据范围:$n,m\le 5\times 10^4$
算法分析
例题中区间的信息没有区间可加性,因此无法用数据结构进行维护,
首先我们分析一下是否可以使用莫队解决,对于区间 $[l,r]$,答案为 $\dfrac{\sum_{i=1}^n \binom{f(i)}{2}}{\binom{r-l+1}{2}}$(其中 $f(i)$ 表示当前区间内颜色 $i$ 出现的次数)。如果要求出区间 $[l+1,r]$ 的答案,只比原先少了一个 $c_l$,我们把分子分母分开考虑:
- 分子:在颜色 $c_l$ 中选择的方案数变少,减少了 $f(c_l)-1$ 种方案数。
- 分母:就是新的区间长度 $r-l$ 中选 $2$ 个数的方案数 $\binom{r-l}{2}$。
对于向其他几种新区间的转移过程也是可以的,读者可以自行尝试分析。接下来我们就来了解一下莫队算法的过程!
排序
把所有的询问离线,把询问左端点 $l_i$ 以 $\Theta(\sqrt{n})$ 的大小进行分块,记第 $i$ 个询问左端点所在的块为 $b_i$,以 $b_i$ 为第一关键字,$r_i$ 为第二关键字进行排序。那么整体的 $b_i$ 是有序的,同一个块中的 $r_i$ 是有序的。
询问
其实这个转移并没有什么技巧,反而非常非常非常暴力!
如果我们已经知道了 $[L,R]$ 的信息,现在需要计算 $[l,r]$ 的答案,那我们直接把 $L$ 暴力移动到 $l$,把 $R$ 暴力移动到 $r$ 即可。
证明
了解完莫队的暴力转移过程,我们就要优美地证明这个过程并不暴力!
离线询问的整个过程中,只有 $L$ 和 $R$ 在不断移动并且两者独立,所以我们分别考虑。
左端点
- 相同块:左端点每次最多移动 $O(\sqrt n)$ 的距离,有 $O(m)$ 个询问。那么同一个块中左端点的移动距离为 $O(m\sqrt n)$。
- 跨越块:左端点每次最多移动 $O(\sqrt n)$ 的距离,有 $O(\sqrt n)$ 次跨越块。那么跨越块导致左端点的移动距离为 $O(n)$。
右端点
- 相同块:右端点每次最多移动 $O(n)$ 的距离,有 $O(\sqrt n)$ 个块。那么同一个块中右端点移动距离为 $O(n\sqrt n)$。
- 跨越块:右端点每次最多移动 $O(n)$ 的距离,有 $O(\sqrt n)$ 次跨越块。那么跨越块导致右端点移动距离为 $O(n\sqrt n)$。
综上所述,莫队算法的复杂度为 $O((n+m)\sqrt n)$。
代码
莫队的代码非常套路,这份代码就当做是一个模板吧!
1 |
|
习题
扩展
如果我们把块的大小设为 $\Theta\left(\dfrac{n}{\sqrt{m}}\right)$,那么莫队算法的复杂度为 $O(n\sqrt m)$(复杂度分析同上)。