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
Table of Content
[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.
#include <iostream>
using namespace std;
// Structure to represent a query range
struct Query
{
int L, R;
};
// Prints sum of all query ranges. m is number of queries
// n is the size of the array.
void printQuerySums(int arr[], int n, Query q[], int m)
{
for (int i = 0; i < m; i++)
{
int L = q[i].L, R = q[i].R;
int sum = 0;
for (int j = L; j <= R; j++)
sum += arr[j];
cout << "Sum of [" << L << ", " << R << "] is " << sum << endl;
}
}
int main()
{
int arr[] = {1, 1, 2, 1, 3, 4, 5, 2, 8};
int n = sizeof(arr) / sizeof(arr[0]);
Query q[] = {{0, 4}, {1, 3}, {2, 4}};
int m = sizeof(q) / sizeof(q[0]);
printQuerySums(arr, n, q, m);
return 0;
}
import java.util.*;
// Class to represent a query range
class Query{
int L;
int R;
Query(int L, int R){
this.L = L;
this.R = R;
}
}
class GFG
{
// Prints sum of all query ranges. m is number of queries
// n is the size of the array.
static void printQuerySums(int arr[], int n, ArrayList<Query> q, int m)
{
// One by one compute sum of all queries
for (int i=0; i<m; i++)
{
// Left and right boundaries of current range
int L = q.get(i).L, R = q.get(i).R;
// Compute sum of current query range
int sum = 0;
for (int j=L; j<=R; j++)
sum += arr[j];
// Print sum of current query range
System.out.println("Sum of [" + L +
", " + R + "] is " + sum);
}
}
public static void main(String argv[])
{
int arr[] = {1, 1, 2, 1, 3, 4, 5, 2, 8};
int n = arr.length;
ArrayList<Query> q = new ArrayList<Query>();
q.add(new Query(0,4));
q.add(new Query(1,3));
q.add(new Query(2,4));
int m = q.size();
printQuerySums(arr, n, q, m);
}
}
# Function to compute and print sum of each query
def print_query_sum(arr, queries):
# Traverse through each query
for q in queries:
L, R = q
s = 0
# Compute sum of current query range
for i in range(L, R + 1):
s += arr[i]
# Print result
print("Sum of", q, "is", s)
if __name__ == "__main__":
# Input array
arr = [1, 1, 2, 1, 3, 4, 5, 2, 8]
# Query ranges
queries = [[0, 4], [1, 3], [2, 4]]
# Function call
print_query_sum(arr, queries)
using System;
using System.Collections;
// Class to represent a query range
public class Query
{
public int L;
public int R;
public Query(int L, int R)
{
this.L = L;
this.R = R;
}
}
class GFG{
// Prints sum of all query ranges. m
//is number of queries n is the size
// of the array.
static void printQuerySums(int []arr, int n,
ArrayList q, int m)
{
// One by one compute sum of all queries
for(int i = 0; i < m; i++)
{
// Left and right boundaries of
// current range
int L = ((Query)q[i]).L,
R = ((Query)q[i]).R;
// Compute sum of current query range
int sum = 0;
for(int j = L; j <= R; j++)
sum += arr[j];
// Print sum of current query range
Console.Write("Sum of [" + L + ", " +
R + "] is " + sum + "\n");
}
}
public static void Main(string []argv)
{
int []arr = { 1, 1, 2, 1, 3, 4, 5, 2, 8 };
int n = arr.Length;
ArrayList q = new ArrayList();
q.Add(new Query(0, 4));
q.Add(new Query(1, 3));
q.Add(new Query(2, 4));
int m = q.Count;
printQuerySums(arr, n, q, m);
}
}
using System;
using System.Collections;
// Class to represent a query range
public class Query
{
public int L;
public int R;
public Query(int L, int R)
{
this.L = L;
this.R = R;
}
}
class GFG{
// Prints sum of all query ranges. m
//is number of queries n is the size
// of the array.
static void printQuerySums(int []arr, int n,
ArrayList q, int m)
{
// One by one compute sum of all queries
for(int i = 0; i < m; i++)
{
// Left and right boundaries of
// current range
int L = ((Query)q[i]).L,
R = ((Query)q[i]).R;
// Compute sum of current query range
int sum = 0;
for(int j = L; j <= R; j++)
sum += arr[j];
// Print sum of current query range
Console.Write("Sum of [" + L + ", " +
R + "] is " + sum + "\n");
}
}
public static void Main(string []argv)
{
int []arr = { 1, 1, 2, 1, 3, 4, 5, 2, 8 };
int n = arr.Length;
ArrayList q = new ArrayList();
q.Add(new Query(0, 4));
q.Add(new Query(1, 3));
q.Add(new Query(2, 4));
int m = q.Count;
printQuerySums(arr, n, q, m);
}
}
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.
#include <iostream>
using namespace std;
// Variable to represent block size. This is made global
// so compare() of sort can use it.
int block;
// Structure to represent a query range
struct Query
{
int L, R;
};
// Function used to sort all queries so that all queries
// of the same block are arranged together and within a block,
// queries are sorted in increasing order of R values.
bool compare(Query x, Query y)
{
// Different blocks, sort by block.
if (x.L/block != y.L/block)
return x.L/block < y.L/block;
// Same block, sort by R value
return x.R < y.R;
}
// Prints sum of all query ranges. m is number of queries
// n is size of array a[].
void queryResults(int a[], int n, Query q[], int m)
{
// Find block size
block = (int)sqrt(n);
// Sort all queries so that queries of same blocks
// are arranged together.
sort(q, q + m, compare);
// Initialize current L, current R and current sum
int currL = 0, currR = 0;
int currSum = 0;
// Traverse through all queries
for (int i=0; i<m; i++)
{
// L and R values of current range
int L = q[i].L, R = q[i].R;
// Remove extra elements of previous range. For
// example if previous range is [0, 3] and current
// range is [2, 5], then a[0] and a[1] are subtracted
while (currL < L)
{
currSum -= a[currL];
currL++;
}
// Add Elements of current Range
while (currL > L)
{
currSum += a[currL-1];
currL--;
}
while (currR <= R)
{
currSum += a[currR];
currR++;
}
// Remove elements of previous range. For example
// when previous range is [0, 10] and current range
// is [3, 8], then a[9] and a[10] are subtracted
while (currR > R+1)
{
currSum -= a[currR-1];
currR--;
}
// Print sum of current range
cout << "Sum of [" << L << ", " << R
<< "] is " << currSum << endl;
}
}
int main()
{
int a[] = {1, 1, 2, 1, 3, 4, 5, 2, 8};
int n = sizeof(a)/sizeof(a[0]);
Query q[] = {{0, 4}, {1, 3}, {2, 4}};
int m = sizeof(q)/sizeof(q[0]);
queryResults(a, n, q, m);
return 0;
}
import java.util.*;
// Class to represent a query range
class Query{
int L;
int R;
Query(int L, int R){
this.L = L;
this.R = R;
}
}
class GFG{
// Prints sum of all query ranges. m is number of queries
// n is size of array a[].
static void queryResults(int a[], int n, ArrayList<Query> q, int m){
// Find block size
int block = (int) Math.sqrt(n);
// Sort all queries so that queries of same blocks
// are arranged together.
Collections.sort(q, new Comparator<Query>(){
// Function used to sort all queries so that all queries
// of the same block are arranged together and within a block,
// queries are sorted in increasing order of R values.
public int compare(Query x, Query y){
// Different blocks, sort by block.
if (x.L/block != y.L/block)
return (x.L < y.L ? -1 : 1);
// Same block, sort by R value
return (x.R < y.R ? -1 : 1);
}
});
// Initialize current L, current R and current sum
int currL = 0, currR = 0;
int currSum = 0;
// Traverse through all queries
for (int i=0; i<m; i++)
{
// L and R values of current range
int L = q.get(i).L, R = q.get(i).R;
// Remove extra elements of previous range. For
// example if previous range is [0, 3] and current
// range is [2, 5], then a[0] and a[1] are subtracted
while (currL < L)
{
currSum -= a[currL];
currL++;
}
// Add Elements of current Range
while (currL > L)
{
currSum += a[currL-1];
currL--;
}
while (currR <= R)
{
currSum += a[currR];
currR++;
}
// Remove elements of previous range. For example
// when previous range is [0, 10] and current range
// is [3, 8], then a[9] and a[10] are subtracted
while (currR > R+1)
{
currSum -= a[currR-1];
currR--;
}
// Print sum of current range
System.out.println("Sum of [" + L +
", " + R + "] is " + currSum);
}
}
public static void main(String argv[]){
ArrayList<Query> q = new ArrayList<Query>();
q.add(new Query(0,4));
q.add(new Query(1,3));
q.add(new Query(2,4));
int a[] = {1, 1, 2, 1, 3, 4, 5, 2, 8};
queryResults(a, a.length, q, q.size());
}
}
import math
# Function that accepts array and list of queries
# and prints sum of each query
def query_results(arr, queries):
# Sort all queries in increasing order of R
queries.sort(key=lambda x: x[1])
# Initialize current L, current R and current sum
currL, currR, currSum = 0, 0, 0
# Traverse through all queries
for q in queries:
L, R = q
# Remove extra elements from previous range
while currL < L:
currSum -= arr[currL]
currL += 1
# Add elements when moving left boundary backward
while currL > L:
currSum += arr[currL - 1]
currL -= 1
# Add elements of current range
while currR <= R:
currSum += arr[currR]
currR += 1
# Remove elements when right boundary shrinks
while currR > R + 1:
currSum -= arr[currR - 1]
currR -= 1
# Print the result
print("Sum of", q, "is", currSum)
if __name__ == "__main__":
arr = [1, 1, 2, 1, 3, 4, 5, 2, 8]
queries = [[0, 4], [1, 3], [2, 4]]
query_results(arr, queries)
using System;
using System.Collections.Generic;
class GFG
{
// Variable to represent block size. This is made global
// so compare() of sort can use it.
public static int block;
// Structure to represent a query range
public struct Query
{
public int L;
public int R;
public Query(int l, int r)
{
L = l;
R = r;
}
}
// Function used to sort all queries so that all queries
// of the same block are arranged together and within a
// block, queries are sorted in increasing order of R
// values.
public class Comparer : IComparer<Query> {
public int Compare(Query x, Query y)
{
int ret = (int)(x.L / block)
.CompareTo((int)(y.L / block));
return ret != 0 ? ret : x.R.CompareTo(y.R);
}
}
// Prints sum of all query ranges. m is number of
// queries n is size of array a[].
static void queryResults(int[] a, int n, List<Query> q,
int m)
{
// Find block size
block = (int)(Math.Sqrt(n));
// Sort all queries so that queries of same blocks
// are arranged together.
q.Sort(new Comparer());
// Initialize current L, current R and current sum
int currL = 0, currR = 0;
int currSum = 0;
// Traverse through all queries
for (int i = 0; i < m; i++) {
// L and R values of current range
int L = q[i].L, R = q[i].R;
// Remove extra elements of previous range. For
// example if previous range is [0, 3] and
// current range is [2, 5], then a[0] and a[1]
// are subtracted
while (currL < L) {
currSum -= a[currL];
currL++;
}
// Add Elements of current Range
while (currL > L) {
currSum += a[currL - 1];
currL--;
}
while (currR <= R) {
currSum += a[currR];
currR++;
}
// Remove elements of previous range. For
// example when previous range is [0, 10] and
// current range is [3, 8], then a[9] and a[10]
// are subtracted
while (currR > R + 1) {
currSum -= a[currR - 1];
currR--;
}
// Print sum of current range
Console.WriteLine("Sum of [{0}, {1}] is {2}", L,
R, currSum);
}
}
static void Main(string[] args)
{
int[] a = { 1, 1, 2, 1, 3, 4, 5, 2, 8 };
int n = a.Length;
List<Query> q = new List<Query>();
q.Add(new Query(0, 4));
q.Add(new Query(1, 3));
q.Add(new Query(2, 4));
int m = q.Count;
queryResults(a, n, q, m);
}
}
function queryResults(arr, Q) {
// Sort all queries so that all queries in the increasing order of R values
Q.sort((a, b) => a[1] - b[1]);
// Initialize current L, current R and current sum
let currL = 0;
let currR = 0;
let currSum = 0;
// Traverse through all queries
for (let i = 0; i < Q.length; i++) {
const L = Q[i][0];
const R = Q[i][1];
// Remove extra elements from previous range
// if previous range is [0, 3] and current
// range is [2, 5], then a[0] and a[1] are subtracted
while (currL < L) {
currSum -= arr[currL];
currL++;
}
// Add elements of current range
while (currL > L) {
currSum += arr[currL - 1];
currL--;
}
while (currR <= R) {
currSum += arr[currR];
currR++;
}
// Remove elements of previous range
// when previous range is [0, 10] and current range
// is [3, 8], then a[9] and a[10] are subtracted
while (currR > R + 1) {
currSum -= arr[currR - 1];
currR--;
}
// Print the sum of current range
console.log(`Sum of ${Q[i]} is ${currSum}`);
}
}
const arr = [1, 1, 2, 1, 3, 4, 5, 2, 8];
const Q = [[1, 3], [0, 4], [2, 4]];
queryResults(arr, Q);
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.