Lam NguyenLibrary

Library · Data Structures · range queries

Mo's Algorithm

/*
Mo's Algorithm
*/
#pragma once
#include <bits/stdc++.h>
using namespace std;

template <typename T>
struct Mo {
    struct Query {
        int L, R, id;
    };

    int BLOCK_SIZE;
    vector<T> A;
    vector<Query> Q;

    Mo(const vector<T>& a, int n_queries) {
        A = a;
        int n = A.size();
        BLOCK_SIZE = ceil(sqrt(n));
        Q.reserve(n_queries);
    }

    void add_query(int L, int R, int id) {
        Q.push_back({L, R, id});
    }

    // expand then shrink
    // add(++mo_R)/add(--mo_L) and remove(mo_R--)/remove(mo_L--)
    inline void process_queries() {
        // by block of L, then R (zig-zag on odd blocks): the window moves O((n + q) sqrt n)
        sort(Q.begin(), Q.end(), [&](const Query& a, const Query& b) {
            int ba = a.L / BLOCK_SIZE, bb = b.L / BLOCK_SIZE;
            if (ba != bb) return ba < bb;
            return (ba & 1) ? a.R > b.R : a.R < b.R;
        });

        // window size = mo_R - mo_L + 1 = 0 (empty)
        int mo_L = 0, mo_R = -1;
        for (int i = 0; i < Q.size(); i++) {
            int L = Q[i].L, R = Q[i].R;

            // expand
            while (mo_L > L) add(--mo_L);
            while (mo_R < R) add(++mo_R);

            // shrink
            while (mo_L < L) remove(mo_L--);
            while (mo_R > R) remove(mo_R--);

            // process answer here
            // ans[Q[i].id] = ...
        }
    }
    
    inline void add(int idx) {}
    inline void remove(int idx) {}
};
from math import isqrt

# Mo's algorithm: answer offline range queries by moving one window [mo_L, mo_R].
# The caller keeps the window state:
#   add(i)     A[i] enters the window
#   remove(i)  A[i] leaves the window
#   answer()   the answer for the current window


class Mo:
    def __init__(self, a: list):
        self.A = a
        self.BLOCK_SIZE = max(1, isqrt(len(a)))
        self.Q = [] # (L, R, id), inclusive

    def add_query(self, L: int, R: int, id: int) -> None:
        self.Q.append((L, R, id))

    # answers indexed by query id
    def process_queries(self, add, remove, answer) -> list:
        B = self.BLOCK_SIZE
        # sort by block of L, then R (zig-zag on odd blocks), so the window moves O((n + q) sqrt n)
        order = sorted(self.Q, key=lambda q: (q[0] // B, q[1] if (q[0] // B) % 2 == 0 else -q[1]))
        ans = [None] * len(self.Q)
        mo_L, mo_R = 0, -1 # empty window
        for L, R, id in order:
            # expand first, so the window never turns inside out
            while mo_L > L:
                mo_L -= 1
                add(mo_L)
            while mo_R < R:
                mo_R += 1
                add(mo_R)
            # then shrink
            while mo_L < L:
                remove(mo_L)
                mo_L += 1
            while mo_R > R:
                remove(mo_R)
                mo_R -= 1
            ans[id] = answer()
        return ans