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:
#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;
} |
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);
}
}
} |
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()
|
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);
}
}
} |
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);
} |
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:
#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;
} |
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;
}
}
} |
# 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()
|
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 |
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 |
Answers without update: 149 196 1247 2065 1743 267 715 85 1071 1022
Time Complexity: O(Q * sqrt(N))
Auxiliary Space: O(sqrt(N))