Algorithm Search⌘K

Proofs: Mo's algorithm

These are the human proofs of Mo's algorithm, at full length.

Mo's algorithm makes O(n sqrt q) moves full

This proof shows: complexity.

Let 1≤B≤n1 \le B \le n be the block size, k=⌈n/B⌉k = \lceil n / B \rceil the number of blocks, and let the queries be sorted by the key (⌊l/B⌋,r)(\lfloor l / B \rfloor, r). Let (l1,r1),…,(lq,rq)(l_1, r_1), \ldots, (l_q, r_q) be the queries in this order and (l0,r0)=(0,0)(l_0, r_0) = (0, 0). A round makes exactly ∣lt−lt−1∣+∣rt−rt−1∣|l_t - l_{t-1}| + |r_t - r_{t-1}| calls of ADD and REMOVE, because each loop moves one end one step towards its target. So the total cost is

C=∑t=1q∣lt−lt−1∣+∑t=1q∣rt−rt−1∣.C = \sum_{t=1}^{q} |l_t - l_{t-1}| + \sum_{t=1}^{q} |r_t - r_{t-1}|.

Lemma 1 (the right end). ∑t∣rt−rt−1∣≤2nk\sum_t |r_t - r_{t-1}| \le 2 n k.

Proof. Group the rounds by the block bt=⌊lt/B⌋b_t = \lfloor l_t / B \rfloor; at most kk groups are non-empty. Inside a group the rtr_t are non-decreasing, so the moves inside the group add up to at most nn. The first round of each group moves RR by at most nn. Over at most kk groups the sum is at most 2nk2 n k. □\square

Lemma 2 (the left end). ∑t∣lt−lt−1∣≤qB+2n+2B\sum_t |l_t - l_{t-1}| \le q B + 2 n + 2 B.

Proof. If bt=bt−1b_t = b_{t-1}, both left ends lie in the same block of BB positions, so ∣lt−lt−1∣<B|l_t - l_{t-1}| < B, and these rounds add up to less than qBq B. Otherwise bt>bt−1b_t > b_{t-1} (with b0=0b_0 = 0 for the start), and 0<lt−lt−1<(bt−bt−1+1)B0 < l_t - l_{t-1} < (b_t - b_{t-1} + 1) B. The blocks increase along these rounds, so the sum of bt−bt−1b_t - b_{t-1} over them is at most k−1k - 1, and there are at most kk such rounds. They add up to less than (k−1+k)B<2kB<2(n+B)(k - 1 + k) B < 2 k B < 2(n + B), because kB<n+Bk B < n + B. □\square

Theorem. With B=max⁡(1,min⁡(n,⌈n/q⌉))B = \max(1, \min(n, \lceil n / \sqrt q \rceil)) and 1≤q≤n21 \le q \le n^2, the algorithm makes O(nq)O(n \sqrt q) calls of ADD and REMOVE, and with an O(1)O(1) cost per call it runs in O(nq+qlog⁡q)O(n \sqrt q + q \log q) time.

Proof. By Lemmas 1 and 2, C≤2n⌈n/B⌉+qB+4nC \le 2 n \lceil n / B \rceil + q B + 4n. With B≥n/qB \ge n / \sqrt q, n⌈n/B⌉≤n(q+1)n \lceil n / B \rceil \le n (\sqrt q + 1). With B≤n/q+1B \le n / \sqrt q + 1, qB≤nq+q≤2nqq B \le n \sqrt q + q \le 2 n \sqrt q because q≤n2q \le n^2 gives q≤n\sqrt q \le n. So C=O(nq+n)=O(nq)C = O(n \sqrt q + n) = O(n \sqrt q). Sorting the keys takes O(qlog⁡q)O(q \log q) time, and the other lines of the loop take O(q)O(q). □\square

The bound of Lemma 1 is tight. For each block bb, take the two queries (bB,bB+1)(bB, bB + 1) and (bB,n)(bB, n). In sorted order, RR climbs to nn in each block and falls back to (b+1)B+1(b + 1) B + 1 at the start of the next one, so ∑t∣rt−rt−1∣≥∑b=0k−2(n−(b+1)B−1)\sum_t |r_t - r_{t-1}| \ge \sum_{b=0}^{k-2} (n - (b + 1) B - 1), which is about nk/2n k / 2.

Mo's algorithm returns the value of each query range full

This proof shows: correctness, termination.

Let A[0…n−1]A[0 \ldots n-1] be the array and Q0,…,Qq−1Q_0, \ldots, Q_{q-1} the queries, with Qj=(lj,rj)Q_j = (l_j, r_j) and 0≤lj≤rj≤n0 \le l_j \le r_j \le n. Write W(L,R)W(L, R) for the multiset {A[L],…,A[R−1]}\{A[L], \ldots, A[R-1]\}. The holes satisfy the contract: if SS is the state of W(L,R)W(L, R), then ADD(L−1)(L - 1) makes it the state of W(L−1,R)W(L - 1, R) for L≥1L \ge 1, ADD(R)(R) makes it the state of W(L,R+1)W(L, R + 1) for R<nR < n, REMOVE(L)(L) makes it the state of W(L+1,R)W(L + 1, R) for L<RL < R, REMOVE(R−1)(R - 1) makes it the state of W(L,R−1)W(L, R - 1) for L<RL < R, and ANSWER returns f(W(L,R))f(W(L, R)).

Invariant (I). Before each round of line L3 and after each step of lines L4 to L7, 0≤L≤R≤n0 \le L \le R \le n and SS is the state of W(L,R)W(L, R).

Lemma 1. (I) holds before the first round.

Proof. Line L2 sets L=R=0L = R = 0 and the state of the empty window, which is W(0,0)W(0, 0). □\square

Lemma 2. If (I) holds at the start of a round for the query (l,r)(l, r), then it holds after each step of lines L4 to L7, every call of ADD and REMOVE meets the contract, and at the end L=lL = l and R=rR = r.

Proof. Let (L0,R0)(L_0, R_0) be the window at the start.

Line L4 runs while L>lL > l. Each step has L≥1L \ge 1 because L>l≥0L > l \ge 0, sets L←L−1L \leftarrow L - 1 and calls ADD on the new LL, which is the item that enters on the left. LL stays at most RR because it decreases. The loop stops with L=min⁡(L0,l)L = \min(L_0, l).

Line L5 runs while R<rR < r. Each step has R<r≤nR < r \le n, calls ADD(R)(R), the item that enters on the right, and sets R←R+1R \leftarrow R + 1. The loop stops with R=max⁡(R0,r)R = \max(R_0, r).

Now L≤lL \le l and R≥rR \ge r. Line L6 runs while L<lL < l. During this loop L<l≤r≤RL < l \le r \le R, so L<RL < R and REMOVE(L)(L) meets the contract; then L←L+1≤l≤RL \leftarrow L + 1 \le l \le R. The loop stops with L=lL = l.

Line L7 runs while R>rR > r. During this loop R>r≥l=LR > r \ge l = L, so L<RL < R, the step sets R←R−1R \leftarrow R - 1 and REMOVE removes A[R]A[R], the last item of the old window, and R≥r≥LR \ge r \ge L afterwards. The loop stops with R=rR = r.

Each step keeps (I) by the contract of the hole it calls. □\square

Theorem 1 (correctness). For each jj, ans[j]=f(A[lj…rj−1])\mathrm{ans}[j] = f(A[l_j \ldots r_j - 1]).

Proof. Line L1 lists every index jj exactly once, because it sorts a permutation. By Lemma 1 and Lemma 2, by induction over the rounds, (I) holds at line L8 of the round of jj with [L,R)=[lj,rj)[L, R) = [l_j, r_j). So ANSWER returns f(W(lj,rj))f(W(l_j, r_j)), and line L8 stores it at index jj. Later rounds write only other indices. □\square

Theorem 2 (termination). The algorithm stops after a finite number of calls of ADD and REMOVE.

Proof. Each of the four loops of a round changes one end by one step towards a fixed target and stops when the target is reached, so a round makes at most ∣L0−l∣+∣R0−r∣+∣l−L0∣+∣r−R0∣≤4n|L_0 - l| + |R_0 - r| + |l - L_0| + |r - R_0| \le 4n calls. There are qq rounds. □\square

Remark (the order of the loops). The proof of Lemma 2 uses that lines L4 and L5 run before lines L6 and L7. If the left end shrinks first, for example from [0,2)[0, 2) to the query [5,8)[5, 8), then LL passes RR, and REMOVE is called on items that are not in the window, which breaks the contract.

Back to Mo's algorithm