Count of divisors of product of an Array in range L to R for Q queries

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.

Note: The ranges are 1-positioned.

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

Brute force:

  1. For every Query, iterate array from L to R and calculate the product of elements in the given range.
  2. Calculate the total number of divisors of that product.

Time Complexity: Q * N * (log (N))

Efficient Approach: The idea is to use Segment Tree to solve this problem.

  1. We can’t store the product of array from L to R because of the constraints so we can store the power of prime in segment tree.
  2. For every query, we merge the trees according to the query.

Construction of Segment Tree from given array

  • We start with a segment arr[0 . . . n-1].
  • Every time we divide the current segment into two halves, if it has not yet become a segment of length 1.
  • Then call the same procedure on both halves, and for each such segment, we store the power of primes in the corresponding node.
  • All levels of the constructed segment tree will be completely filled except the last level.
  • Also, the tree will be a Full Binary Tree because we always divide segments into two halves at every level.
  • Since the constructed tree is always a full binary tree with n leaves, there will be n-1 internal nodes. So the total number of nodes will be 2*n – 1.

Below is the implementation of the above approach.

filter_none

edit
close

play_arrow

link
brightness_4
code

#include <bits/stdc++.h>
using namespace std;
#define ll long long int
#define MOD 1000000007
  
#define MAXN 1000001
  
// Array to store the precomputed prime numbers
int spf[MAXN];
  
// Function signatures
void merge(
    vector<pair<int, int> > tree[], int treeIndex);
void sieve();
vector<int> getFactorization(
    int x);
void buildTree(
    vector<pair<int, int> > tree[], int treeIndex,
    int start, int end, int arr[]);
int query(
    vector<pair<int, int> > tree[], int treeIndex,
    int start, int end,
    int left, int right);
vector<pair<int, int> > mergeUtil(
    vector<pair<int, int> > op1,
    vector<pair<int, int> > op2);
vector<pair<int, int> > queryUtil(
    vector<pair<int, int> > tree[], int treeIndex,
    int start, int end,
    int left, int right);
  
// Function to use Sieve to compute
// and store prime numbers
void sieve()
{
    spf[1] = 1;
    for (int i = 2; i < MAXN; i++)
        spf[i] = i;
  
    for (int i = 4; i < MAXN; i += 2)
        spf[i] = 2;
  
    for (int i = 3; i * i < MAXN; i++) {
        if (spf[i] == i) {
            for (int j = i * i; j < MAXN; j += i)
                if (spf[j] == j)
                    spf[j] = i;
        }
    }
}
  
// Function to find the
// prime factors of a value
vector<int> getFactorization(int x)
{
  
    // Vectore to store the prime factors
    vector<int> ret;
  
    // For every prime factor of x,
    // push it to the vector
    while (x != 1) {
  
        ret.push_back(spf[x]);
        x = x / spf[x];
    }
  
    // Return the prime factors
    return ret;
}
  
// Recursive function to build segment tree
// by merging the power pf primes
void buildTree(vector<pair<int, int> > tree[],
               int treeIndex, int start,
               int end, int arr[])
{
  
    // Base case
    if (start == end) {
  
        int val = arr[start];
        vector<int> primeFac
            = getFactorization(val);
  
        for (auto it : primeFac) {
            int power = 0;
            while (val % it == 0) {
                val = val / it;
                power++;
            }
  
            // Storing powers of prime
            // in tree at treeIndex
            tree[treeIndex]
                .push_back({ it, power });
        }
        return;
    }
  
    int mid = (start + end) / 2;
  
    // Building the left tree
    buildTree(tree, 2 * treeIndex, start,
              mid, arr);
  
    // Building the right tree
    buildTree(tree, 2 * treeIndex + 1,
              mid + 1, end, arr);
  
    // Merges the right tree and left tree
    merge(tree, treeIndex);
  
    return;
}
  
// Function to merge the right
// and the left tree
void merge(vector<pair<int, int> > tree[],
           int treeIndex)
{
    int index1 = 0;
    int index2 = 0;
    int len1 = tree[2 * treeIndex].size();
    int len2 = tree[2 * treeIndex + 1].size();
  
    // Fill this array in such a way such that values
    // of prime remain sorted similar to mergesort
    while (index1 < len1 && index2 < len2) {
  
        // If both of the elements are same
        if (tree[2 * treeIndex][index1].first
            == tree[2 * treeIndex + 1][index2].first) {
  
            tree[treeIndex].push_back(
                { tree[2 * treeIndex][index1].first,
                  tree[2 * treeIndex][index1].second
                      + tree[2 * treeIndex + 1]
                            [index2]
                                .second });
            index1++;
            index2++;
        }
  
        // If the element on the left part
        // is greater than the right part
        else if (tree[2 * treeIndex][index1].first
                 > tree[2 * treeIndex + 1][index2].first) {
  
            tree[treeIndex].push_back(
                { tree[2 * treeIndex + 1][index2].first,
                  tree[2 * treeIndex + 1][index2].second });
            index2++;
        }
  
        // If the element on the left part
        // is less than the right part
        else {
            tree[treeIndex].push_back(
                { tree[2 * treeIndex][index1].first,
                  tree[2 * treeIndex][index1].second });
            index1++;
        }
    }
  
    // Insert the leftover elements
    // from the left part
    while (index1 < len1) {
        tree[treeIndex].push_back(
            { tree[2 * treeIndex][index1].first,
              tree[2 * treeIndex][index1].second });
        index1++;
    }
  
    // Insert the leftover elements
    // from the right part
    while (index2 < len2) {
        tree[treeIndex].push_back(
            { tree[2 * treeIndex + 1][index2].first,
              tree[2 * treeIndex + 1][index2].second });
        index2++;
    }
    return;
}
  
// Function similar to merge() method
// But here it takes two vectors and
// return a vector of power of primes
vector<pair<int, int> > mergeUtil(
    vector<pair<int, int> > op1,
    vector<pair<int, int> > op2)
{
    int index1 = 0;
    int index2 = 0;
    int len1 = op1.size();
    int len2 = op2.size();
    vector<pair<int, int> > res;
  
    while (index1 < len1 && index2 < len2) {
  
        if (op1[index1].first == op2[index2].first) {
            res.push_back({ op1[index1].first,
                            op1[index1].second
                                + op2[index2].second });
            index1++;
            index2++;
        }
  
        else if (op1[index1].first > op2[index2].first) {
            res.push_back({ op2[index2].first,
                            op2[index2].second });
            index2++;
        }
        else {
            res.push_back({ op1[index1].first,
                            op1[index1].second });
            index1++;
        }
    }
    while (index1 < len1) {
        res.push_back({ op1[index1].first,
                        op1[index1].second });
        index1++;
    }
    while (index2 < len2) {
        res.push_back({ op2[index2].first,
                        op2[index2].second });
        index2++;
    }
    return res;
}
  
// Function similar to query() method
// as in segment tree
// Here also the result is merged
vector<pair<int, int> > queryUtil(
    vector<pair<int, int> > tree[],
    int treeIndex, int start,
    int end, int left, int right)
{
    if (start > right || end < left) {
        tree[0].clear();
        return tree[0];
    }
    if (start >= left && end <= right) {
        return tree[treeIndex];
    }
    int mid = (start + end) / 2;
  
    vector<pair<int, int> > op1
        = queryUtil(tree, 2 * treeIndex,
                    start, mid,
                    left, right);
    vector<pair<int, int> > op2
        = queryUtil(tree, 2 * treeIndex + 1,
                    mid + 1, end,
                    left, right);
  
    return mergeUtil(op1, op2);
}
  
// Function to return the number of divisors
// of product of array from left to right
int query(vector<pair<int, int> > tree[],
          int treeIndex, int start,
          int end, int left, int right)
{
    vector<pair<int, int> > res
        = queryUtil(tree, treeIndex,
                    start, end,
                    left, right);
  
    int sum = 1;
    for (auto it : res) {
        sum *= (it.second + 1);
        sum %= MOD;
    }
    return sum;
}
  
// Function to solve queries
void solveQueries(
    int arr[], int len,
    int l, int r)
{
  
    // Calculate the size of segment tree
    int x = (int)(ceil(log2(len)));
  
    // Maximum size of segment tree
    int max_size = 2 * (int)pow(2, x) - 1;
  
    // first of pair is used to store the prime
    // second of pair is use to store its power
    vector<pair<int, int> > tree[max_size];
  
    // building the tree
    buildTree(tree, 1, 0, len - 1, arr);
  
    // Find the required number of divisors
    // of product of array from l to r
    cout << query(tree, 1, 0,
                  len - 1, l - 1, r - 1)
         << endl;
}
  
// Driver code
int main()
{
  
    // Precomputing the prime numbers using sieve
    sieve();
  
    int arr[] = { 5, 2, 3, 1, 4 };
    int len = sizeof(arr) / sizeof(arr[0]);
    int queries = 2;
    int Q[queries][2] = { { 2, 4 }, { 1, 5 } };
  
    // Solve the queries
    for (int i = 0; i < queries; i++) {
        solveQueries(arr, len, Q[i][0], Q[i][1]);
    }
    return 0;
}

chevron_right


Output:

4
16

Time Complexity: Q * (log (N))

GeeksforGeeks has prepared a complete interview preparation course with premium videos, theory, practice problems, TA support and many more features. Please refer Placement 100 for details




My Personal Notes arrow_drop_up

Check out this Author's contributed articles.

If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

Please Improve this article if you find anything incorrect by clicking on the "Improve Article" button below.