Open In App

Mo’s Algo with update and without update

Last Updated : 12 Feb, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Mo’s Algorithm is a truly versatile algorithm that finds utility in a vast array of problems requiring querying an array or a list for a subarray. Now, let me tell you, this algorithm owes its efficiency to a very specific order of processing the queries, which significantly reduces the time required to access the array elements.

How does Mo’s Algorithm work? 

  • The algorithm starts by dividing the input array into blocks of a fixed size, and each block comprises sqrt(N) elements, where N denotes the size of the input array. Then, the algorithm sorts the requests according to the block index to which they belong.
  • Now, the interesting part is that the queries are handled in sorted order, and a two-pointer technique facilitates the query’s transition from one block to another. 
  • No need to process every single element in each block, which speeds up the overall query time substantially. 
  • The time complexity of Mo’s Algorithm is O((N+Q)*sqrt(N)), where N is the size of the input array, and Q is the number of queries.

Mo’s Algorithm with Updates

Mo’s Algorithm with updates is an extension of the original algorithm that permits efficient updates to the input array while maintaining the same query time complexity. This is accomplished using a technique called Mo’s Algorithm with Lazy Propagation.  Mo’s Algorithm with updates utilizes a lazy propagation technique to defer the update operation until a query necessitates it. The algorithm maintains a distinct array that monitors the pending updates, and the query time complexity remains identical to that of the original algorithm.

  • Mo’s algorithm to efficiently respond to array range queries. 
  • It divides the array into sqrt(N) blocks, where N is the length of the array. 
  • The method then runs through each query, computing partial sums inside blocks and summing entire blocks. 

Below is the Python implementation for Mo’s algorithm with updates:

C++




#include <bits/stdc++.h>
using namespace std;
 
// Nikunj Sonigara
// Function to perform Mo's algorithm with updates
vector<int> mo_algorithm_with_update(vector<int>& array,
                                     vector<pair<int, int>>& queries,
                                     vector<pair<int, int>>& updates) {
    int block_size = sqrt(array.size());
    vector<vector<int>> blocks(array.size() / block_size);
 
    // Dividing the input array into blocks
    for (int i = 0; i < array.size(); i++) {
        blocks[i / block_size].push_back(array[i]);
    }
 
    // Vector to store the answers for each query
    vector<int> answers;
 
    // Processing each query
    for (auto query : queries) {
        int start = query.first;
        int end = query.second;
        int start_block = start / block_size;
        int end_block = end / block_size;
 
        // If the query lies entirely within one block
        if (start_block == end_block) {
            int answer = accumulate(array.begin() + start, array.begin() + end + 1, 0);
            answers.push_back(answer);
        } else {
            // Summing values for the subarrays of different blocks
            int answer = accumulate(array.begin() + start, array.begin() +
                                    (start_block + 1) * block_size, 0);
            for (int i = start_block + 1; i < end_block; i++) {
                answer += accumulate(blocks[i].begin(), blocks[i].end(), 0);
            }
            answer += accumulate(array.begin() +
                                 end_block * block_size, array.begin() + end + 1, 0);
            answers.push_back(answer);
        }
    }
 
    // Applying updates to the array and blocks
    for (auto update : updates) {
        int index = update.first;
        int value = update.second;
        int block_index = index / block_size;
        array[index] = value;
        blocks[block_index] = vector<int>(array.begin() + block_index * block_size, array.begin() + (block_index + 1) * block_size);
    }
 
    return answers;
}
 
int main() {
    // Generating a random array of size 100
    vector<int> array(100);
    random_device rd;
    mt19937 gen(rd());
    uniform_int_distribution<> dis(0, 100);
    generate(array.begin(), array.end(), [&]() { return dis(gen); });
 
    // Generating random queries
    vector<pair<int, int>> queries(10);
    generate(queries.begin(), queries.end(), [&]() {
        int start = dis(gen) % 100;
        int end = dis(gen) % 100;
        return make_pair(start, end);
    });
 
    // Generating random updates
    vector<pair<int, int>> updates(5);
    generate(updates.begin(), updates.end(), [&]() {
        int index = dis(gen) % 100;
        int value = dis(gen) % 100;
        return make_pair(index, value);
    });
 
    // Applying Mo's algorithm with updates
    vector<int> answers_with_update = mo_algorithm_with_update(array, queries, updates);
 
    // Printing the answers
    cout << "Answers with update:" << endl;
    for (int answer : answers_with_update) {
        cout << answer << endl;
    }
 
    return 0;
}


Java




import java.util.*;
import java.util.stream.Collectors;
 
class Pair<A, B> {
    private A first;
    private B second;
 
    public Pair(A first, B second) {
        this.first = first;
        this.second = second;
    }
 
    public A getFirst() {
        return first;
    }
 
    public B getSecond() {
        return second;
    }
}
 
public class MoAlgorithmWithUpdates {
 
    // Function to implement Mo's Algorithm with updates
    public static List<Integer> moAlgorithmWithUpdate(List<Integer> array, List<Pair<Integer, Integer>> queries, List<Pair<Integer, Integer>> updates) {
        // Determine the block size for dividing the array
        int block_size = (int) Math.sqrt(array.size());
        List<List<Integer>> blocks = new ArrayList<>();
 
        // Divide the input array into blocks
        for (int i = 0; i < array.size(); i += block_size) {
            // Create blocks of size 'block_size' or less for the last block
            blocks.add(array.subList(i, Math.min(i + block_size, array.size())));
        }
 
        List<Integer> answers = new ArrayList<>();
 
        // Process each query
        for (Pair<Integer, Integer> query : queries) {
            int start = query.getFirst();
            int end = query.getSecond();
            int start_block = start / block_size;
            int end_block = end / block_size;
            int answer = 0;
 
            if (start_block == end_block) {
                // Query falls within a single block, so calculate directly from the array
                for (int i = start; i <= end; i++) {
                    answer += array.get(i);
                }
            } else {
                // Query spans multiple blocks
                for (int i = start; i < (start_block + 1) * block_size; i++) {
                    answer += array.get(i);
                }
                for (int i = start_block + 1; i < end_block; i++) {
                    // Sum the values in blocks between start_block and end_block
                    answer += blocks.get(i).stream().mapToInt(Integer::intValue).sum();
                }
                for (int i = end_block * block_size; i <= end; i++) {
                    answer += array.get(i);
                }
            }
            answers.add(answer);
        }
 
        // Apply updates to the input array and corresponding blocks
        for (Pair<Integer, Integer> update : updates) {
            int index = update.getFirst();
            int value = update.getSecond();
            int block_index = index / block_size;
 
            // Update the value in the array
            array.set(index, value);
 
            // Update the corresponding block with the modified value
            blocks.set(block_index, new ArrayList<>(array.subList(block_index * block_size, Math.min((block_index + 1) * block_size, array.size()))));
        }
 
        return answers;
    }
 
    public static void main(String[] args) {
        // Generating a random array of size 100
        List<Integer> array = new Random().ints(100, 0, 101).boxed().collect(Collectors.toList());
 
        // Generating random queries
        List<Pair<Integer, Integer>> queries = new ArrayList<>();
        Random rand = new Random();
        for (int i = 0; i < 10; i++) {
            int start = rand.nextInt(100);
            int end = rand.nextInt(100);
            queries.add(new Pair<>(Math.min(start, end), Math.max(start, end)));
        }
 
        // Generating random updates
        List<Pair<Integer, Integer>> updates = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            int index = rand.nextInt(100);
            int value = rand.nextInt(101);
            updates.add(new Pair<>(index, value));
        }
 
        // Applying Mo's algorithm with updates
        List<Integer> answersWithUpdate = moAlgorithmWithUpdate(array, queries, updates);
 
        // Printing the answers
        System.out.println("Answers with update:");
        for (int answer : answersWithUpdate) {
            System.out.println(answer);
        }
    }
}


Python3




import random
import math
 
 
def mo_algorithm_with_update(array, queries, updates):
    """
    Answer range queries using Mo's algorithm with update.
 
    Args:
        array: The input array.
        queries: The range queries.
        updates: The update operations.
 
    Returns:
        The answers to the range queries after applying the updates.
    """
 
    # Divide the input array into blocks of
    # size sqrt(N).
    block_size = int(math.sqrt(len(array)))
    blocks = [[] for _ in range(len(array) // block_size)]
    for i in range(len(array)):
        blocks[i // block_size].append(array[i])
 
    # Create a data structure to store the
    # answers to the range queries.
    answers = []
 
    # Answer the range queries.
    for query in queries:
        start, end = query
        start_block = start // block_size
        end_block = end // block_size
 
        if start_block == end_block:
            # If the query is contained within
            # a single block.
            answer = sum(array[start:end + 1])
        else:
            # If the query spans multiple
            # blocks.
            # Partial sum in the start block
            answer = sum(array[start:(start_block + 1) * block_size])
            # Sum of complete blocks
            answer += sum(sum(blocks[start_block + 1:end_block], []))
            # Partial sum in the end block
            answer += sum(array[end_block * block_size:end + 1])
 
        answers.append(answer)
 
    # Apply the update operations.
    for update in updates:
        index, value = update
        block_index = index // block_size
        array[index] = value
        blocks[block_index] = array[block_index * block_size:(block_index + 1) * block_size]
 
    return answers
 
# Driver code
 
 
def main():
    # Create an input array.
    array = [random.randint(0, 100) for _ in range(100)]
 
    # Create a list of range queries.
    queries = []
    for _ in range(10):
        queries.append((random.randint(0, 99), random.randint(0, 99)))
 
    # Create a list of update operations.
    updates = []
    for _ in range(5):
        updates.append((random.randint(0, 99), random.randint(0, 100)))
 
    # Answer the range queries using Mo's algorithm with update.
    answers_with_update = mo_algorithm_with_update(array, queries, updates)
    print("Answers with update:")
    for answer in answers_with_update:
        print(answer)
 
 
if __name__ == "__main__":
    main()


C#




using System;
using System.Collections.Generic;
using System.Linq;
 
// Make Pair class public
public class Pair<A, B>
{
    private A first;
    private B second;
 
    public Pair(A first, B second)
    {
        this.first = first;
        this.second = second;
    }
 
    public A GetFirst()
    {
        return first;
    }
 
    public B GetSecond()
    {
        return second;
    }
}
 
public class MoAlgorithmWithUpdates
{
    // Function to implement Mo's Algorithm with updates
    public static List<int> MoAlgorithmWithUpdate(List<int> array, List<Pair<int, int>> queries, List<Pair<int, int>> updates)
    {
        // Determine the block size for dividing the array
        int blockSize = (int)Math.Sqrt(array.Count);
        List<List<int>> blocks = new List<List<int>>();
 
        // Divide the input array into blocks
        for (int i = 0; i < array.Count; i += blockSize)
        {
            // Create blocks of size 'blockSize' or less for the last block
            blocks.Add(array.GetRange(i, Math.Min(blockSize, array.Count - i)));
        }
 
        List<int> answers = new List<int>();
 
        // Process each query
        foreach (Pair<int, int> query in queries)
        {
            int start = query.GetFirst();
            int end = query.GetSecond();
            int startBlock = start / blockSize;
            int endBlock = end / blockSize;
            int answer = 0;
 
            if (startBlock == endBlock)
            {
                // Query falls within a single block, so calculate directly from the array
                for (int i = start; i <= end; i++)
                {
                    answer += array[i];
                }
            }
            else
            {
                // Query spans multiple blocks
                for (int i = start; i < (startBlock + 1) * blockSize; i++)
                {
                    answer += array[i];
                }
                for (int i = startBlock + 1; i < endBlock; i++)
                {
                    // Sum the values in blocks between startBlock and endBlock
                    answer += blocks[i].Sum();
                }
                for (int i = endBlock * blockSize; i <= end; i++)
                {
                    answer += array[i];
                }
            }
            answers.Add(answer);
        }
 
        // Apply updates to the input array and corresponding blocks
        foreach (Pair<int, int> update in updates)
        {
            int index = update.GetFirst();
            int value = update.GetSecond();
            int blockIndex = index / blockSize;
 
            // Update the value in the array
            array[index] = value;
 
            // Update the corresponding block with the modified value
            int startRange = blockIndex * blockSize;
            int endRange = Math.Min((blockIndex + 1) * blockSize, array.Count);
            blocks[blockIndex] = new List<int>(array.GetRange(startRange, endRange - startRange));
        }
 
        return answers;
    }
 
    public static void Main(string[] args)
    {
        // Generating a random array of size 100
        List<int> array = Enumerable.Range(0, 100).Select(_ => new Random().Next(0, 101)).ToList();
 
        // Generating random queries
        List<Pair<int, int>> queries = new List<Pair<int, int>>();
        Random rand = new Random();
        for (int i = 0; i < 10; i++)
        {
            int start = rand.Next(100);
            int end = rand.Next(100);
            queries.Add(new Pair<int, int>(Math.Min(start, end), Math.Max(start, end)));
        }
 
        // Generating random updates
        List<Pair<int, int>> updates = new List<Pair<int, int>>();
        for (int i = 0; i < 5; i++)
        {
            int index = rand.Next(100);
            int value = rand.Next(101);
            updates.Add(new Pair<int, int>(index, value));
        }
 
        // Applying Mo's algorithm with updates
        List<int> answersWithUpdate = MoAlgorithmWithUpdate(array, queries, updates);
 
        // Printing the answers
        Console.WriteLine("Answers with update:");
        foreach (int answer in answersWithUpdate)
        {
            Console.WriteLine(answer);
        }
    }
}


Javascript




class Pair {
    constructor(first, second) {
        this.first = first;
        this.second = second;
    }
 
    getFirst() {
        return this.first;
    }
 
    getSecond() {
        return this.second;
    }
}
 
function moAlgorithmWithUpdate(array, queries, updates) {
    // Determine the block size for dividing the array
    const blockSize = Math.sqrt(array.length);
    const blocks = [];
 
    // Divide the input array into blocks
    for (let i = 0; i < array.length; i += blockSize) {
        // Create blocks of size 'blockSize' or less for the last block
        blocks.push(array.slice(i, Math.min(i + blockSize, array.length)));
    }
 
    const answers = [];
 
    // Process each query
    for (const query of queries) {
        const start = query.getFirst();
        const end = query.getSecond();
        const startBlock = Math.floor(start / blockSize);
        const endBlock = Math.floor(end / blockSize);
        let answer = 0;
 
        if (startBlock === endBlock) {
            // Query falls within a single block, so calculate directly from the array
            for (let i = start; i <= end; i++) {
                answer += array[i];
            }
        } else {
            // Query spans multiple blocks
            for (let i = start; i < (startBlock + 1) * blockSize; i++) {
                answer += array[i];
            }
            for (let i = startBlock + 1; i < endBlock; i++) {
                // Sum the values in blocks between startBlock and endBlock
                answer += blocks[i].reduce((acc, curr) => acc + curr, 0);
            }
            for (let i = endBlock * blockSize; i <= end; i++) {
                answer += array[i];
            }
        }
        answers.push(answer);
    }
 
    // Apply updates to the input array and corresponding blocks
    for (const update of updates) {
        const index = update.getFirst();
        const value = update.getSecond();
        const blockIndex = Math.floor(index / blockSize);
 
        // Update the value in the array
        array[index] = value;
 
        // Update the corresponding block with the modified value
        blocks[blockIndex] = array.slice(blockIndex * blockSize, Math.min((blockIndex + 1) * blockSize, array.length));
    }
 
    return answers;
}
 
// Generating a random array of size 100
const array = Array.from({ length: 100 }, () => Math.floor(Math.random() * 101));
 
// Generating random queries
const queries = [];
for (let i = 0; i < 10; i++) {
    const start = Math.floor(Math.random() * 100);
    const end = Math.floor(Math.random() * 100);
    queries.push(new Pair(Math.min(start, end), Math.max(start, end)));
}
 
// Generating random updates
const updates = [];
for (let i = 0; i < 5; i++) {
    const index = Math.floor(Math.random() * 100);
    const value = Math.floor(Math.random() * 101);
    updates.push(new Pair(index, value));
}
 
// Applying Mo's algorithm with updates
const answersWithUpdate = moAlgorithmWithUpdate(array, queries, updates);
 
// Printing the answers
console.log("Answers with update:");
for (const answer of answersWithUpdate) {
    console.log(answer);
}


Output

Answers with update:
416
422
723
2841
380
412
716
259
399
426


Time Complexity: O(Q * sqrt(N))
Auxiliary Space: O(sqrt(N))

Mo’s Algorithm without Updates

Mo’s Algorithm without Updates is the original algorithm which we discussed earlier in this the queries are handled in a sorted order, and a two-pointer technique facilitates the query’s transition from one block to another. No need to process every single element in each block, which speeds up the overall query time substantially.. This algorithm finds its utility in cases where the input array is static, and no updates are necessary. 

  • Mo’s Algorithm without updates is a solid choice when you’re dealing with static data and need to perform range queries like calculating the sum of elements in a subarray, locating the minimum or maximum element in a subarray, or determining the median of elements in a subarray.
  • The provided code uses Mo’s algorithm without an update to efficiently respond to array range queries. It divides the array into sqrt(N) blocks, where N is the length of the array. 
  • The method then runs through each query, computing partial sums inside blocks and summing entire blocks. 

Below is the Python implementation for Mo’s algorithm without updates:

C++




#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <ctime>
#include <numeric>
 
std::vector<int> moAlgorithmWithoutUpdate(const std::vector<int>& array, const std::vector<std::pair<int, int>>& queries) {
    int n = array.size();
 
    // Divide the input array into blocks of size sqrt(N).
    int blockSize = static_cast<int>(std::sqrt(n));
    std::vector<std::vector<int>> blocks(n / blockSize + 1, std::vector<int>());
    for (int i = 0; i < n; ++i) {
        blocks[i / blockSize].push_back(array[i]);
    }
 
    // Create a data structure to store the answers to the range queries.
    std::vector<int> answers;
 
    // Answer the range queries.
    for (const auto& query : queries) {
        int start = query.first;
        int end = query.second;
        int startBlock = start / blockSize;
        int endBlock = end / blockSize;
 
        if (startBlock == endBlock) {
            // If the query is contained within a single block.
            int answer = 0;
            for (int i = start; i <= end; ++i) {
                answer += array[i];
            }
            answers.push_back(answer);
        } else {
            // If the query spans multiple blocks.
            // Partial sum in the start block
            int answer = 0;
            for (int i = start; i < (startBlock + 1) * blockSize; ++i) {
                answer += array[i];
            }
            // Sum of complete blocks
            for (int i = startBlock + 1; i < endBlock; ++i) {
                answer += std::accumulate(blocks[i].begin(), blocks[i].end(), 0);
            }
            // Partial sum in the end block
            for (int i = endBlock * blockSize; i <= end; ++i) {
                answer += array[i];
            }
            answers.push_back(answer);
        }
    }
 
    return answers;
}
 
int main() {
    // Create an input array.
    std::vector<int> array;
    std::srand(std::time(0));
    for (int i = 1; i <= 100; ++i) {
        array.push_back(i);
    }
 
    // Create a vector of range queries.
    std::vector<std::pair<int, int>> queries;
    for (int i = 0; i < 10; ++i) {
        queries.push_back({std::rand() % 100, std::rand() % 100});
    }
 
    // Answer the range queries using Mo's algorithm without update.
    std::vector<int> answersWithoutUpdate = moAlgorithmWithoutUpdate(array, queries);
    std::cout << "Answers without update:" << std::endl;
    for (int answer : answersWithoutUpdate) {
        std::cout << answer << std::endl;
    }
 
    return 0;
}


Java




import java.util.ArrayList;
import java.util.List;
 
public class Main {
 
    // Function to answer range queries using Mo's algorithm without update
    public static List<Integer> moAlgorithmWithoutUpdate(List<Integer> array, List<Pair<Integer, Integer>> queries) {
        int n = array.size();
 
        // Divide the input array into blocks of size sqrt(N).
        int blockSize = (int) Math.sqrt(n);
        List<List<Integer>> blocks = new ArrayList<>();
        for (int i = 0; i < n / blockSize + 1; ++i) {
            blocks.add(new ArrayList<>());
        }
        for (int i = 0; i < n; ++i) {
            blocks.get(i / blockSize).add(array.get(i));
        }
 
        // Create a list to store the answers to the range queries.
        List<Integer> answers = new ArrayList<>();
 
        // Answer the range queries.
        for (Pair<Integer, Integer> query : queries) {
            int start = query.getFirst();
            int end = query.getSecond();
            int startBlock = start / blockSize;
            int endBlock = end / blockSize;
 
            if (startBlock == endBlock) {
                // If the query is contained within a single block.
                int answer = 0;
                for (int i = start; i <= end; ++i) {
                    answer += array.get(i);
                }
                answers.add(answer);
            } else {
                // If the query spans multiple blocks.
                // Partial sum in the start block
                int answer = 0;
                for (int i = start; i < (startBlock + 1) * blockSize; ++i) {
                    answer += array.get(i);
                }
                // Sum of complete blocks
                for (int i = startBlock + 1; i < endBlock; ++i) {
                    answer += blocks.get(i).stream().mapToInt(Integer::intValue).sum();
                }
                // Partial sum in the end block
                for (int i = endBlock * blockSize; i <= end; ++i) {
                    answer += array.get(i);
                }
                answers.add(answer);
            }
        }
 
        return answers;
    }
 
    public static void main(String[] args) {
        // Create an input array.
        List<Integer> array = new ArrayList<>();
        for (int i = 1; i <= 100; ++i) {
            array.add(i);
        }
 
        // Create a list of range queries.
        List<Pair<Integer, Integer>> queries = new ArrayList<>();
        for (int i = 0; i < 10; ++i) {
            queries.add(new Pair<>((int) (Math.random() * 100), (int) (Math.random() * 100)));
        }
 
        // Answer the range queries using Mo's algorithm without update.
        List<Integer> answersWithoutUpdate = moAlgorithmWithoutUpdate(array, queries);
        System.out.println("Answers without update:");
        for (int answer : answersWithoutUpdate) {
            System.out.println(answer);
        }
    }
 
    // Pair class to represent a pair of elements
    static class Pair<T, U> {
        private final T first;
        private final U second;
 
        public Pair(T first, U second) {
            this.first = first;
            this.second = second;
        }
 
        public T getFirst() {
            return first;
        }
 
        public U getSecond() {
            return second;
        }
    }
}


Python3




# Python Implementation
import random
import math
 
 
def mo_algorithm_without_update(array, queries):
    """
    Answer range queries using Mo's algorithm without update.
 
    Args:
        array: The input array.
        queries: The range queries.
 
    Returns:
        The answers to the range queries.
    """
 
    # Divide the input array into blocks of
    # size sqrt(N).
    block_size = int(math.sqrt(len(array)))
    blocks = [[] for _ in range(len(array) // block_size)]
    for i in range(len(array)):
        blocks[i // block_size].append(array[i])
 
    # Create a data structure to store the
    # answers to the range queries.
    answers = []
 
    # Answer the range queries.
    for query in queries:
        start, end = query
        start_block = start // block_size
        end_block = end // block_size
 
        if start_block == end_block:
            # If the query is contained within
            # a single block.
            answer = sum(array[start:end + 1])
        else:
            # If the query spans multiple blocks.
            # Partial sum in the start block
            answer = sum(array[start:(start_block + 1) * block_size])
            # Sum of complete blocks
            answer += sum(sum(blocks[start_block + 1:end_block], []))
            # Partial sum in the end block
            answer += sum(array[end_block * block_size:end + 1])
 
        answers.append(answer)
 
    return answers
 
# Driver code
 
 
def main():
    # Create an input array.
    array = [random.randint(0, 100) for _ in range(100)]
 
    # Create a list of range queries.
    queries = []
    for _ in range(10):
        queries.append((random.randint(0, 99), random.randint(0, 99)))
 
    # Create a list of update operations.
    updates = []
    for _ in range(5):
        updates.append((random.randint(0, 99), random.randint(0, 100)))
 
    # Answer the range queries using Mo's algorithm
    # without update.
    answers_without_update = mo_algorithm_without_update(array, queries)
    print("Answers without update:")
    for answer in answers_without_update:
        print(answer)
 
 
if __name__ == "__main__":
    main()


C#




using System;
using System.Collections.Generic;
using System.Linq;
 
class MainClass {
    static List<int> MoAlgorithmWithoutUpdate(List<int> array, List<Tuple<int, int>> queries) {
        int n = array.Count;
 
        // Divide the input array into blocks of size sqrt(N).
        int blockSize = (int)Math.Sqrt(n);
        List<List<int>> blocks = new List<List<int>>();
        for (int i = 0; i < n; i += blockSize) {
            blocks.Add(array.Skip(i).Take(blockSize).ToList());
        }
 
        // Create a data structure to store the answers to the range queries.
        List<int> answers = new List<int>();
 
        // Answer the range queries.
        foreach (var query in queries) {
            int start = query.Item1;
            int end = query.Item2;
            int startBlock = start / blockSize;
            int endBlock = end / blockSize;
 
            if (startBlock == endBlock) {
                // If the query is contained within a single block.
                int answer = 0;
                for (int i = start; i <= end; ++i) {
                    answer += array[i];
                }
                answers.Add(answer);
            } else {
                // If the query spans multiple blocks.
                // Partial sum in the start block
                int answer = 0;
                for (int i = start; i < (startBlock + 1) * blockSize; ++i) {
                    answer += array[i];
                }
                // Sum of complete blocks
                for (int i = startBlock + 1; i < endBlock; ++i) {
                    answer += blocks[i].Sum();
                }
                // Partial sum in the end block
                for (int i = endBlock * blockSize; i <= end; ++i) {
                    answer += array[i];
                }
                answers.Add(answer);
            }
        }
 
        return answers;
    }
 
    public static void Main(string[] args) {
        // Create an input array.
        List<int> array = Enumerable.Range(1, 100).ToList();
 
        // Create a list of range queries.
        List<Tuple<int, int>> queries = new List<Tuple<int, int>>();
        Random random = new Random();
        for (int i = 0; i < 10; ++i) {
            queries.Add(new Tuple<int, int>(random.Next(100), random.Next(100)));
        }
 
        // Answer the range queries using Mo's algorithm without update.
        List<int> answersWithoutUpdate = MoAlgorithmWithoutUpdate(array, queries);
        Console.WriteLine("Answers without update:");
        foreach (int answer in answersWithoutUpdate) {
            Console.WriteLine(answer);
        }
    }
}
// this code is contributed by utkarsh


Javascript




function moAlgorithmWithoutUpdate(array, queries) {
    const n = array.length;
 
    // Divide the input array into blocks of size sqrt(N).
    const blockSize = Math.sqrt(n);
    const blocks = Array.from({ length: Math.ceil(n / blockSize) }, () => []);
 
    for (let i = 0; i < n; ++i) {
        blocks[Math.floor(i / blockSize)].push(array[i]);
    }
 
    // Create a data structure to store the answers to the range queries.
    const answers = [];
 
    // Answer the range queries.
    for (const [start, end] of queries) {
        const startBlock = Math.floor(start / blockSize);
        const endBlock = Math.floor(end / blockSize);
 
        if (startBlock === endBlock) {
            // If the query is contained within a single block.
            let answer = 0;
            for (let i = start; i <= end; ++i) {
                answer += array[i];
            }
            answers.push(answer);
        } else {
            // If the query spans multiple blocks.
            // Partial sum in the start block
            let answer = 0;
            for (let i = start; i < (startBlock + 1) * blockSize; ++i) {
                answer += array[i];
            }
            // Sum of complete blocks
            for (let i = startBlock + 1; i < endBlock; ++i) {
                answer += blocks[i].reduce((acc, val) => acc + val, 0);
            }
            // Partial sum in the end block
            for (let i = endBlock * blockSize; i <= end; ++i) {
                answer += array[i];
            }
            answers.push(answer);
        }
    }
 
    return answers;
}
 
// Create an input array.
const array = Array.from({ length: 100 }, (_, i) => i + 1);
 
// Create a vector of range queries.
const queries = Array.from({ length: 10 }, () => [Math.floor(Math.random() * 100), Math.floor(Math.random() * 100)]);
 
// Answer the range queries using Mo's algorithm without update.
const answersWithoutUpdate = moAlgorithmWithoutUpdate(array, queries);
console.log("Answers without update:");
answersWithoutUpdate.forEach(answer => console.log(answer));
//This code is contributed by Adarsh


Output

Answers without update:
149
196
1247
2065
1743
267
715
85
1071
1022




Time Complexity: O(Q * sqrt(N))
Auxiliary Space: O(sqrt(N))



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads