莫队
这真是一种非常神奇的算法啊~~
首先我们要知道莫队算法的基本作用。这个算法基本上可以在 O ( n n ) O(n\sqrt{n}) O(nn) 的时间内处理一些简单的区间问题。而它处理区间问题的方式是通过我们已经处理过的一些区间来实现的。举个例子:比如我已经知道了在区间 [ L 1 , R 1 ] [L_1, R_1] [L1,R1] 的答案。那么我们又要知道区间 [ L 2 , R 2 ] [L_2, R_2] [L2,R2] 的答案,莫队算法会选择每次把一个端点向外扩展(假设这个扩展的复杂度是 O ( 1 ) O(1) O(1) 的)那么我们就需要 O ( ∣ L 1 − L 2 ∣ ) O(|L_1 - L_2|) O(∣L1−L2∣) 的时间把左端点扩展到 L 2 L_2 L2 处,我们又需要 O ( R 1 − R 2 ) O(R_1 - R_2) O(R1−R2) 的时间把右端点扩展到 L 2 L_2 L2 处。所以每一次处理的时间复杂度就是 O ( ∣ L 1 − L 2 ∣ + ∣ R 1 − R 2 ∣ ) O(|L_1 - L_2| + |R_1 - R_2|) O(∣L1−L2∣+∣R1−R2∣)。
看这个时间复杂度的形式是不是很眼熟: O ( ∣ L 1 − L 2 ∣ + ∣ R 1 − R 2 ∣ ) O(|L_1 - L_2| + |R_1 - R_2|) O(∣L1−L2∣+∣R1−R2∣),是的,假如我们把每一个区间看成一个在平面直角坐标系中的点 ( L , R ) (L, R) (L,R) 的话,那么我们每次处理的复杂度就是两点之间的曼哈顿距离。如果我们通过某种次序,使得我们走过所有点的曼哈顿路径长度尽可能小,那么我们的时间复杂度也就会尽可能的小。但是我们知道要求最小曼哈顿距离的算法是一个 N P NP NP 问题。所以我们只能退而求其次,找一中不那么优秀但是已经很优秀的算法,也就是莫队算法。
首先,我们把整个要查询的数组分块 (不会还有人不知道分块是什么吧,不会吧不会吧),然后以左端点所在的块为第一关键字,以右端点作为第二关键字把所有询问排序(这个操作的前提是这个问题是可以离线的,如果强制在线的话就不能用莫队了)。然后我们就可以一次处理每一个问题。下面我们来证明一下这个复杂度问什么是正确的的。
首先,我们知道,这个算法的复杂度也就是左端点和右端点分别移动了多少次。那么我们首先统计右端点的移动次数。由于第二关键字为右端点所在位置,所以对于同一个块中的左端点来说,右端点下标应该是不降的,所以在同一个块里右端点最多移动 n n n 次,且总共就只有 n \sqrt{n} n 个块,那么右端点最多就移动 O ( n n ) O(n\sqrt{n}) O(nn) 次。
然后对于左端点来说,按顺序处理时左端点的块的序号是不降的。又因为左端点所在块改变时最多移动 n n n,且每次询问左端点移动次数不超过 n \sqrt{n} n。那么左端点移动次数就不会超过 n + m n n + m\sqrt{n} n+mn( m m m 是询问次数)。
综上所述,莫队算法的时间复杂度也就是 O ( n + ( n + m ) n ) O(n + (n + m)\sqrt{n}) O(n+(n+m)n)。