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:
9
24
Input: arr[] = {5, 2, 3, 1, 4}, Q = {{2, 4}, {1, 5}}
Output:
4
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.
-
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.
-
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
-
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:
-
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)
-
-
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)
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