Computing Library › Classical Algorithms
Classical Algorithms

Mo's Algorithm

Answering many offline range queries efficiently by reordering them to minimize the movement of two pointers.

Offline query reordering

Mo's algorithm answers q range queries on a static array by processing them offline in a carefully chosen order so that a running window can be adjusted incrementally. Instead of recomputing each range from scratch, it maintains an answer for the current window [l, r] and slides the endpoints to the next query. The reordering bounds the total pointer movement.

Block ordering

Kronos motion — classical

Queries are sorted by the block of their left endpoint (blocks of size about sqrt(n)), and within a block by right endpoint. With this order, the right pointer moves monotonically within each block and the left pointer stays within one block at a time. Total movement is O((n + q) * sqrt(n)), so each query costs about O(sqrt(n)) amortized, provided add and remove of a single element are O(1).

Comparator and loop

python
import math

def mos(queries, n):
    block = int(math.sqrt(n)) + 1
    queries.sort(key=lambda q: (q.l // block,
                                q.r if (q.l//block) % 2 == 0 else -q.r))
    cl, cr = 0, -1
    for q in queries:
        while cr < q.r: cr += 1; add(cr)
        while cl > q.l: cl -= 1; add(cl)
        while cr > q.r: remove(cr); cr -= 1
        while cl < q.l: remove(cl); cl += 1
        q.answer = current_answer()

Requirements and variants

When to use it

Reach for Mo's algorithm when a query cannot be decomposed by a segment tree (for example, count of distinct values in a range) but can be maintained incrementally, and all queries are known ahead of time.