Mo's Algorithm Secondary Offline
Overview
Sometimes we encounter problems that seem suitable for Mo's algorithm, but their single transition is not \(O(1)\). In such cases, directly applying Mo's algorithm, even with adjusted block sizes, will lead to incorrect complexity.
If the contribution of each transition to the answer can be differenced, we can break down these transitions, process them offline, and handle them in batches using other algorithms.
Let \(f(x, l, r)\) denote the contribution of \(x\) to \([l, r]\).
For example, when expanding the current interval \([l, r]\) to \([l, r + 1]\), we need \(f(a_{r + 1}, l, r)\). If it can be differenced, we can write it as \(f(a_{r + 1}, 1, r) - f(a_{r + 1}, 1, l - 1)\). The first term can be preprocessed for each \(r\), and the latter term can be stored offline at the corresponding \(l - 1\), then processed by scanning line from small to large. Other transition directions can be handled similarly.
This technique of further offloading transitions within the already offline Mo's algorithm is called Mo's Algorithm with Second Offline.
Let's understand it through concrete problems.
Example Problems
Luogu P5047 [Ynoi2019 Mock Contest] Yuno loves sqrt technology II
You are given a sequence \(a\) of length \(n\) and \(m\) queries. Each query asks for the number of inversions in an interval.
Constraints: \(1 \leq n,m \leq 10^5\), \(0 \leq a_i \leq 10^9\).
Direct Mo's algorithm requires at least \(O(\log n)\) per transition. Observation shows that each transition requires the information "the rank of a number within a certain interval", which can be differenced with respect to the interval. Thus, consider second offline.
Expanding \([l, r]\) to \([l, r + 1]\) requires the number of elements greater than \(a_{r + 1}\) in \([l, r]\). Let \(f(x, r)\) be the number of elements greater than \(a_x\) in \([1, r]\), and \(g(x, r)\) be the number of elements smaller than \(a_x\) in \([1, r]\). Then the change in answer can be written as \(f(r + 1, r) - f(r + 1, l - 1)\).
Similarly, shrinking \([l, r]\) to \([l, r - 1]\) gives \(-f(r, r - 1) + f(r, l - 1)\); expanding \([l, r]\) to \([l - 1, r]\) gives \(g(l - 1, r) - g(l - 1, l - 2)\); shrinking \([l, r]\) to \([l + 1, r]\) gives \(- g(l, r) + g(l, l - 1)\).
For terms like \(f(x, x - 1)\) and \(g(x, x - 1)\), we can preprocess them in \(O(n \log n)\) using a Fenwick tree. For the remaining \(f(x, p)\) and \(g(x, p)\) terms, we store \(x\) offline at \(l\) for batch processing.
A space optimization: For each query, the \(x\) values stored at \(p\) form a contiguous segment. Thus, we don't need to store every single move; only the adjusted segment is needed. This reduces space from total moves \(O(n\sqrt{m})\) to number of queries \(O(m)\).
Now we handle the second offline problem: adding numbers to a set and querying the rank of a number in the set. Mo's algorithm moves endpoints \(O(n\sqrt{m})\) times, but the array length is only \(O(n)\). Consider using a value domain block data structure with \(O(\sqrt{n})\) insertion and \(O(1)\) query to solve this.
Thus, we solve the problem in \(O(n \sqrt{m} + n \sqrt{n})\) time and \(O(n + m)\) space.
Finally, note that we compute the change in answer per step, not the answer itself. Prefix sums are needed to obtain the final answers.
Example Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | |
Luogu P5501 [LnOI2019] Come and welcome, go and no farewell
Multiple queries: For interval \([l, r]\), compute the sum of "Abbi values" of all numbers.
The Abbi value of \(a_i\) is defined as: if \(a_i\) is the \(k\)-th smallest in query interval \([l,r]\), then its Abbi value equals \(k \cdot a_i\).
Let \(f(x,r)\) be the sum of numbers greater than \(a_x\) in \([1,r]\), and \(g(x,r)\) be the count of numbers greater than \(a_x\) in \([1,r]\). When moving the right endpoint to the right, the contribution is \(f(r,r-1)-f(r,l-1) + a_r(r-l+ 1-(g(r,r-1)-g(r,l-1)))\). Other directions can be derived similarly.
The terms \(f(r, r - 1)\) and \(g(r, r - 1)\) can still be preprocessed. The rest are stored offline at the other endpoint for scanning line processing. It's clear that we still need to handle \(O(n)\) insertions and \(O(n\sqrt{m})\) rank queries, so again use value domain blocks.
Example Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | |
本页面最近更新:,更新历史
发现错误?想一起完善? 在 GitHub 上编辑此页!
本页面贡献者:OI-wiki
本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用