VOOZH about

URL: https://www.geeksforgeeks.org/dsa/mos-algorithm-query-square-root-decomposition-set-1-introduction/

⇱ MO's Algorithm Introduction - GeeksforGeeks


  • Courses
  • Tutorials
  • Interview Prep

MO's Algorithm Introduction

Last Updated : 19 Feb, 2026

Mo’s Algorithm can be explained using the range sum query problem, where an array and several queries are given. Each query contains a range [L,R][L, R][L,R], and we need to calculate the sum of elements within that range.

Example:

Input: arr[] = {1, 1, 2, 1, 3, 4, 5, 2, 8}, Query = [0, 4], [1, 3], [2, 4]
Output: Sum of arr[] elements in range [0, 4] is 8
Sum of arr[] elements in range [1, 3] is 4
Sum of arr[] elements in range [2, 4] is 6
Explanation:
Query [0, 4] - 1 + 1 + 2 + 1 + 3 = 8
Query [1, 3] - 1 + 2 + 1 = 4
Query [2, 4] - 2 + 1 + 3 = 6

[Naive Approach] – Linearly Compute Sum for Every Query – O(n × m) Time and O(1) Space

For each query [L,R][L, R][L,R], traverse the array from index L to R and compute the sum of elements in that range. Repeat this process for every query.


Output
Sum of [0, 4] is 8
Sum of [1, 3] is 4
Sum of [2, 4] is 6

[Expected Approach] – MO’s Algorithm – O((n + m) × √n) Time and O(n + m) Space

The idea of MO's algorithm is to pre-process all queries so that result of one query can be used in next query. Below are steps.

Steps of MO’s Algorithm

  • Divide the array into blocks of size √n.
  • Sort the queries by the block number of L, and within the same block, sort them by R in increasing order.
  • Process the queries one by one while maintaining a running sum.
  • Let sum represent the result of the previous query; if the new query has a larger R, add the new elements, and if it has a smaller L, remove the extra elements.
  • Update the sum incrementally instead of recomputing it from scratch.

Output
Sum of [1, 3] is 4
Sum of [0, 4] is 8
Sum of [2, 4] is 6

Note: The results may not appear in the same order as the input queries because the queries are sorted. This can be handled by storing original indices.

Why It Works Efficiently

Because queries are sorted in blocks:

  • The pointer for R moves at most O(n × √n) times.
  • The pointer for L moves at most O(m × √n) times.

Time Complexity

  • Sorting queries: O(m log m)
  • Processing queries: O((n + m) × √n)
  • Overall: O((n + m) × √n)

Space Complexity

  • Storing array and queries - O(n + m)

Important Observations: 

  • All queries must be known beforehand so they can be preprocessed.
  • It does not work efficiently when update operations are mixed with queries.
  • It is suitable for problems where each query result can be derived from the previous one (e.g., sum, minimum, maximum).

Note: A simple and more Efficient solution to solve this problem is to compute prefix sum for all elements from 0 to n-1. Let the prefix sum be stored in an array preSum[] (The value of preSum[i] stores sum of arr[0..i]). Once we have built preSum[], we can traverse through all queries one by one. For every query [L, R], we return value of preSum[R] - preSum[L]. Here processing every query takes O(1) time. 

Comment
Article Tags: