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

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.