Algorithm Search⌘K

Proofs: Mo's algorithm with updates

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

Mo's algorithm with updates makes O(qB + n^2/B + m n^2/B^2) moves full

This proof shows: complexity.

Let n≥1n \ge 1 be the length of the array, qq the number of queries, mm the number of updates, B≥1B \ge 1 the block size and k=⌈n/B⌉k = \lceil n / B \rceil. The queries are sorted by the key (⌊l/B⌋,⌊r/B⌋,t)(\lfloor l / B \rfloor, \lfloor r / B \rfloor, t), with 0≤l≤r≤n0 \le l \le r \le n and 0≤t≤m0 \le t \le m. A group is a maximal run of queries in the sorted order with the same first two key parts (a,b)(a, b). Since a≤b≤⌊n/B⌋≤ka \le b \le \lfloor n / B \rfloor \le k, there are at most (k+1)2(k + 1)^2 groups. Each step of a pointer costs one APPLY, ADD or REMOVE, which is O(1)O(1).

Lemma 1 (time). The pointer TT moves at most 2m(k+1)22m (k + 1)^2 steps.

Proof. Within one group the values of tt are non-decreasing, so TT moves at most mm steps forward inside it in total. On entering a group, TT moves from the last tt of the previous group to the first tt of this one, at most mm steps. Summing over the groups gives the bound. □\square

Lemma 2 (left end). The pointer LL moves O(qB+n)O(q B + n) steps.

Proof. Between consecutive queries with the same left block, LL moves less than BB. Between consecutive queries whose left blocks are a<a′a < a', LL moves at most (a′−a+1)B(a' - a + 1) B. The left blocks are non-decreasing along the order, so these sum to at most n+kB+B≤3n+Bn + k B + B \le 3n + B in total, and the first query costs at most nn. □\square

Lemma 3 (right end). The pointer RR moves O(qB+nk)O(q B + n k) steps.

Proof. Between consecutive queries in the same group, RR moves less than BB. Fix a left block aa. Among its queries the right block is non-decreasing, so the moves between groups with left block aa sum to at most n+kB=O(n)n + kB = O(n), and moving into the first group of aa costs at most nn. Over the at most k+1k + 1 left blocks this is O(nk)O(n k). □\square

Theorem. The total number of steps is O(qB+n2/B+mn2/B2+n)O(q B + n^2 / B + m n^2 / B^2 + n). For q,m≤cnq, m \le c n and B=⌈n2/3⌉B = \lceil n^{2/3} \rceil it is O(n5/3)O(n^{5/3}).

Proof. Add the three lemmas and use k≤n/B+1k \le n / B + 1. With B=n2/3B = n^{2/3}: qB≤cn5/3q B \le c n^{5/3}, n2/B=n4/3n^2 / B = n^{4/3} and mn2/B2≤cn5/3m n^2 / B^2 \le c n^{5/3}. □\square

The sort takes O(qlog⁡q)O(q \log q) comparisons, so the running time is O(qB+n2/B+mn2/B2+n+qlog⁡q)O(q B + n^2 / B + m n^2 / B^2 + n + q \log q).

Mo's algorithm with updates returns the answer of each query on the array of its time full

This proof shows: correctness, termination.

Let A0A_0 be the original array and AjA_j the array after the updates 1,…,j1, \ldots, j, where update jj sets position pjp_j to xjx_j. A query (l,r,t)(l, r, t) asks for ff of the multiset W(l,r,t)={At[k]:l≤k<r}W(l, r, t) = \{A_t[k] : l \le k < r\}. The state of the window is a function of the multiset of values passed to ADD and not yet passed to REMOVE.

Invariant. Between any two steps, (I1) the working array AA equals ATA_T; (I2) for each j≤Tj \le T, U[j]=(pj,Aj−1[pj])U[j] = (p_j, A_{j-1}[p_j]), and for each j>Tj > T, U[j]=(pj,xj)U[j] = (p_j, x_j); (I3) the state holds the multiset {A[k]:L≤k<R}\{A[k] : L \le k < R\}.

At the start, T=0T = 0, A=A0A = A_0, every U[j]=(pj,xj)U[j] = (p_j, x_j), and the window is empty, so the invariant holds.

Lemma 1 (forward step). If the invariant holds with T=j−1T = j - 1, then T←jT \leftarrow j followed by APPLY(j)(j) keeps it.

Proof. By (I2), U[j]=(pj,xj)U[j] = (p_j, x_j). Line L12 sets A[pj]=xjA[p_j] = x_j, so A=AjA = A_j by (I1), and stores the old value Aj−1[pj]A_{j-1}[p_j] in U[j]U[j], which is (I2) for T=jT = j. If L≤pj<RL \le p_j < R, the multiset of the window loses Aj−1[pj]A_{j-1}[p_j] at line L11 and gains Aj[pj]A_j[p_j] at line L13, and no other position changes, so (I3) holds; otherwise the window multiset is unchanged, and so is the state. □\square

Lemma 2 (backward step). If the invariant holds with T=jT = j, then APPLY(j)(j) followed by T←j−1T \leftarrow j - 1 keeps it.

Proof. By (I2), U[j]=(pj,Aj−1[pj])U[j] = (p_j, A_{j-1}[p_j]), and by (I1) A=AjA = A_j, which differs from Aj−1A_{j-1} only at pjp_j. Line L12 writes Aj−1[pj]A_{j-1}[p_j] into A[pj]A[p_j], so A=Aj−1A = A_{j-1}, and stores Aj[pj]=xjA_j[p_j] = x_j in U[j]U[j], which is (I2) for T=j−1T = j - 1. The window part is as in Lemma 1. □\square

Lemma 3 (window step). Each single move of LL or RR in line L7, as in Mo's algorithm, keeps (I3), and lines L5 to L7 do not change AA except through APPLY.

Proof. A move adds or removes one position kk of the window and calls ADD(A[k])(A[k]) or REMOVE(A[k])(A[k]) with the current array value, which is the change of the multiset. Lines L4 to L7 of plain Mo's algorithm add before they remove, so L≤RL \le R throughout and each removed position is in the window. □\square

Theorem. For each query (l,r,t)(l, r, t), the recorded answer is f(W(l,r,t))f(W(l, r, t)), and the algorithm terminates.

Proof. Lines L5 and L6 change TT by one per step towards tt, each by Lemma 1 or Lemma 2, so after them T=tT = t and A=AtA = A_t. Line L7 moves LL to ll and RR to rr by Lemma 3. Then (I3) says the state holds W(l,r,t)W(l, r, t), and line L8 records ff of it. Every loop moves an integer pointer one step towards a fixed target, so each ends, and there are qq queries. □\square

Back to Mo's algorithm with updates