Open In App

Queries for Count of divisors of product of an Array in given range | Set 2 (MO’s Algorithm)

Given an array arr of size N and Q queries of the form [L, R], the task is to find the number of divisors of the product of this array in the given range.
Prerequisite: MO’s Algorithm, Modular Multiplicative Inverse, Prime Factorization using sieve
Examples: 
 

Input: arr[] = {4, 1, 9, 12, 5, 3}, Q = {{1, 3}, {3, 5}} 
Output: 

24
Input: arr[] = {5, 2, 3, 1, 4}, Q = {{2, 4}, {1, 5}} 
Output: 

16 
 




 


Approach:
The idea of MO’s algorithm is to pre-process all queries so that result of one query can be used in the next query. 
Let a[0…n-1] be input array and q[0..m-1] be an array of queries. 
 




  1. Sort all queries in a way that queries with L values from 0 to ?n – 1 are put together, then all queries from ?n to 2×?n – 1, and so on. All queries within a block are sorted in increasing order of R values. 
     

  2. Process all queries one by one in a way that every query uses result computed in the previous query. Let ‘result’ be result of previous query 
     

  3. A number n can be represented as n = 
    *** QuickLaTeX cannot compile formula:
     
    
    *** Error message:
    Error: Nothing to show, formula is empty
    
    , where ai are prime factors and pi are integral power of them. 
    So, for this factorization we have formula to find total number of divisor of n and that is: 
     


  1. In Add function we increment counter array as i.e counter[a[i]]=counter[a[i]]+ pi. Let ‘prev’ stores the previous value of counter[a[i]]. Now as counter array changes, result changes as:
     

  • result = result / (prev + 1) (Dividing by prev+1 neutralizes the effect of previous pi
     

  • result = (result × (counter[pi] + 1) (Now the previous result is neutralized so we multiply with the new count i.e counter[a[i]]+1) 
     

  1.  

  2. In Remove function we decrement the counter array as counter[a[i]] = counter[a[i]] – pi. Now as counter array changes, result changes as:
     

  • result = result / (prev + 1) (Dividing by prev+1 neutralizes the effect of previous pi
     

  • result = (result × (counter[pi] + 1) (Now the previous result is neutralized so we 
    multiply with the new count i.e counter[a[i]]+1) 
     

  1.  


Below is the implementation of the above approach
 

// C++ program to Count the divisors
// of product of an Array in range
// L to R for Q queries
#include <bits/stdc++.h>
using namespace std;
 
#define MAX 1000000
#define MOD 1000000007
#define ll long long int
 
// 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;
};
 
// Store the prime factor of numbers
// till MAX
vector<pair<int, int> > store[MAX + 1];
 
// Initialized to store the count
// of prime factors
int counter[MAX + 1] = {};
 
// Result is Initialized to 1
int result = 1;
 
// Inverse array to store
// inverse of number from 1 to MAX
ll inverse[MAX + 1];
 
// 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;
}
 
// Function to calculate modular
// inverse and storing it in Inverse array
void modularInverse()
{
 
    inverse[0] = inverse[1] = 1;
    for (int i = 2; i <= MAX; i++)
        inverse[i] = inverse[MOD % i]
                    * (MOD - MOD / i)
                      % MOD;
}
 
// Function to use Sieve to compute
// and store prime numbers
void sieve()
{
 
    store[1].push_back({ 1, 0 });
    for (int i = 2; i <= MAX; i++)
    {
        if (store[i].size() == 0)
        {
            store[i].push_back({ i, 1 });
             
            for (int j = 2 * i; j <= MAX; j += i)
            {
                int cnt = 0;
                int x = j;
                while (x % i == 0)
                    cnt++, x /= i;
                store[j].push_back({ i, cnt });
            }
        }
    }
}
 
// Function to Add elements
// of current range
void add(int currL, int a[])
{
    int value = a[currL];
    for (auto it = store[value].begin();
         it != store[value].end(); it++) {
        // it->first is ai
        // it->second is its integral power
        int prev = counter[it->first];
        counter[it->first] += it->second;
        result = (result * inverse[prev + 1])
                 % MOD;
         
        result = (result *
                  (counter[it->first] + 1))
                  % MOD;
    }
}
 
// Function to remove elements
// of previous range
void remove(int currR, int a[])
{
    int value = a[currR];
    for (auto it = store[value].begin();
         it != store[value].end(); it++) {
        // it->first is ai
        // it->second is its integral power
        int prev = counter[it->first];
        counter[it->first] -= it->second;
        result = (result * inverse[prev + 1])
                  % MOD;
        result = (result *
                  (counter[it->first] + 1))
                  % MOD;
    }
}
 
// Function to print the answer.
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 result
    int currL = 0, currR = 0;
 
    for (int i = 0; i < m; i++) {
        // L and R values of current range
        int L = q[i].L, R = q[i].R;
 
        // Add Elements of current range
        while (currR <= R) {
            add(currR, a);
            currR++;
        }
        while (currL > L) {
            add(currL - 1, a);
            currL--;
        }
 
        // Remove element of previous range
        while (currR > R + 1)
 
        {
            remove(currR - 1, a);
            currR--;
        }
        while (currL < L) {
            remove(currL, a);
            currL++;
        }
 
        cout << result << endl;
    }
}
 
// Driver Code
int main()
{
    // Precomputing the prime numbers
    // using sieve
    sieve();
 
    // Precomputing modular inverse of
    // numbers from 1 to MAX
    modularInverse();
 
    int a[] = { 5, 2, 3, 1, 4 };
    int n = sizeof(a) / sizeof(a[0]);
     
    Query q[] = { { 1, 3 }, { 0, 4 } };
     
    int m = sizeof(q) / sizeof(q[0]);
     
    // Answer the queries
    queryResults(a, n, q, m);
    return 0;
}

                    

Output: 
4
16

 

Time Complexity: O(Q×sqrt(N))
Auxiliary Space: O(MAX) where MAX=1000000


Article Tags :