Array Range Queries to count Powerful numbers with updates


Given an array of N integers, the task is to perform the following two operations on the given array:

query(L, R): Print the number of Powerful numbers in the subarray from L to R.
update(i, x) : update the value at index i to x, i.e arr[i] = x

A number N is said to be Powerful Number if, for every prime factor p of it, p2 also divides it.

Prerequisites: Powerful Number, Segment tree

Examples:



Input:
arr = {1, 12, 3, 8, 17, 9}
Query 1: query(L = 1, R = 4)
Query 2: update(i = 1, x = 9)
Query 3: query(L = 1, R = 4)
Output:
1
2
Explanation:
Query 1: Powerful numbers in range arr[1:4] is 8.
Query 2: Powerful numbers in range arr[1:4] after update operation is 9 and 8.

Approach:

Since we need to handle point updates and range queries so we will use a segment tree to solve the problem.

  1. We will precompute all the Powerful numbers till the maximum value that arr[i] can take, say MAX.
    The time complexity of this operation will be O(MAX * sqrt(MAX))
  2. Building the segment tree:

    • The problem can be reduced to subarray sum using segment tree.
    • Now we can build the segment tree where the leaf nodes will represent 1(when a number is a Powerful number) or 0(when a number is not a powerful number). All the internal nodes will have the sum of both of its children.
  3. Point Updates:

    • To update an element we need to look at the interval in which the element is and recurse accordingly on the left or the right child. If the element to be updated is a Powerful number then we update the leaf as 1, else 0.
  4. Range Query:

    • Whenever we get a query from L to R, then we can query the segment tree for the sum of nodes in range L to R, which in turn represents the number of Powerful numbers in the range L to R.

Below is the implementation of the above approach:

filter_none

edit
close

play_arrow

link
brightness_4
code

// C++ Program to find the number
// of Powerful numbers in subarray
// using segment tree
#include <bits/stdc++.h>
using namespace std;
  
#define MAX 100000
  
// Size of segment tree = 2^{log(MAX)+1}
int tree[3 * MAX];
int arr[MAX];
bool powerful[MAX + 1];
  
// Function to check if the 
// number is powerful
bool isPowerful(int n)
{
    // First divide the number
    // repeatedly by 2
    while (n % 2 == 0)
    {
        int power = 0;
        while (n % 2 == 0)
        {
            n /= 2;
            power++;
        }
  
        // If only 2^1 divides 
        // n (not higher powers),
        // then return false
        if (power == 1)
            return false;
    }
  
    // If n is not a power of 2 then 
    // this loop will execute
    // repeat above process
    for (int factor = 3; factor 
         <= sqrt(n); factor += 2)
    {
        // Find highest power of 
        // "factor" that divides n
        int power = 0;
        while (n % factor == 0)
        {
            n = n / factor;
            power++;
        }
  
        // If only factor^1 divides n 
        // (not higher powers),
        // then return false
        if (power == 1)
            return false;
    }
  
    // n must be 1 now if it is not 
    // a prime numenr. Since prime 
    // numbers are not powerful, 
    // we return false if n is not 1.
    return (n == 1);
}
  
// Function to build the array
void BuildArray(int input[], int n)
{
    for (int i = 0; i < n; i++)
    {
        // Check if input[i] is
        // a Powerful number or not
        if (powerful[input[i]])
            arr[i] = 1;
  
        else
            arr[i] = 0;
    }
    return;
  
}
  
// A utility function to get the middle
// index from corner indexes.
int getMid(int s, int e)
{
    return s + (e - s) / 2;
}
  
/* A recursive function that constructs
 Segment Tree for array[ss..se].
  
si --> Index of current node in the 
       segment tree. Initially 0 is 
       passed as root is always
       at index 0.
ss & se --> Starting and ending indexes 
            of the segment represented by
            current node, i.e., st[index]
*/
void constructSTUtil(int si, int ss,
                     int se)
{
    if (ss == se) {
        // If there is one element 
        // in array
        tree[si] = arr[ss];
        return;
    }
  
    // If there are more than one elements,
    // then recur for left and right subtrees 
    // and store the sum of the two
    // values in this node
    else {
        int mid = getMid(ss, se);
          
        constructSTUtil(2 * si + 1, 
                        ss, mid);
          
        constructSTUtil(2 * si + 2, 
                        mid + 1, se);
          
        tree[si] = tree[2 * si + 1]
                  + tree[2 * si + 2];
    }
}
  
/* A recursive function to update the 
nodes which have the given index
in their range.
  
si --> Index of current node in the segment tree.
       Initially 0 is passed as root is always
       at index 0.
ss & se --> Starting and ending indexes of the
            segment represented by current node,
            i.e., st[index]
  
ind --> Index of array to be updated
  
val --> The new value to be updated
  
*/
void updateValueUtil(int si, int ss, int se, 
                     int idx, int val)
{
    // Leaf node
    if (ss == se) {
        tree[si] = tree[si] - arr[idx] + val;
        arr[idx] = val;
    }
    else {
        int mid = getMid(ss, se);
          
        // If idx is in the left child,
        // recurse on the left child
        if (ss <= idx and idx <= mid)
            updateValueUtil(2 * si + 1, ss,
                            mid, idx, val);
  
        // If idx is in the right child,
        // recurse on the right child
        else
            updateValueUtil(2 * si + 2, mid + 1, 
                            se, idx, val);
  
        // Internal node will have the sum 
        // of both of its children
        tree[si] = tree[2 * si + 1]
                   + tree[2 * si + 2];
    }
}
  
/* A recursive function to get the number
of Powerful numbers in a given 
range of array indexes
  
si --> Index of current node in the segment tree.
       Initially 0 is passed as root is always
       at index 0.
ss & se --> Starting and ending indexes of the
            segment represented by current node,
            i.e., st[index]
l & r --> Starting and ending indexes of
          query range
  
  
 */
int queryPowerfulUtil(int si, int ss, int se, 
                      int l, int r)
{
    // If segment of this node is
    // outside the given range
    if (r < ss or se < l) {
        return 0;
    }
    // If segment of this node is a part 
    // of given range, then return the
    // number of composites
    // in the segment
    if (l <= ss and se <= r) {
        return tree[si];
    }
  
    // If a part of this segment
    // overlaps with the given range
    int mid = getMid(ss, se);
    int p1 = queryPowerfulUtil(2 * si + 1, 
                               ss, mid, l,
                               r);
    int p2 = queryPowerfulUtil(2 * si + 2,
                               mid + 1, 
                               se, l, r);
    return (p1 + p2);
}
  
void queryPowerful(int n, int l, int r)
{
    printf("Number of Powerful numbers between %d to %d = %d\n",
           l, r, queryPowerfulUtil(0, 0,
                                   n - 1,
                                   l, r));
}
  
void updateValue(int n, int ind, int val)
{
    // If val is a Powerful number
    // we will update 1 in tree
    if (powerful[val])
        updateValueUtil(0, 0, n - 1,
                        ind, 1);
    else
        updateValueUtil(0, 0, n - 1,
                        ind, 0);
}
  
void precomputePowerful()
{
  
    memset(powerful, false
           sizeof(powerful));
  
    // Computing all Powerful 
    // numbers till MAX
    for (int i = 1; i <= MAX; i++)
    {
        // If the number is 
        // Powerful make 
        // powerful[i] = true
        if (isPowerful(i))
            powerful[i] = true;
    }
}
  
// Driver Code
int main()
{
    // Precompute all the powerful 
    // numbers till MAX
    precomputePowerful();
  
    // Input array
    int input[] = { 4, 5, 18, 27, 40, 144 };
      
    // Size of Input array
    int n = sizeof(input) / sizeof(input[0]);
  
    // Build the array.
    BuildArray(input, n);
      
    // Build segment tree from 
    // given array
    constructSTUtil(0, 0, n - 1);
  
    // Query 1: Query(L = 0, R = 3)
    int l = 0, r = 3;
    queryPowerful(n, l, r);
  
    // Query 2: Update(i = 1, x = 9), 
    // i.e Update input[i] to x
    int i = 1;
    int val = 9;
    updateValue(n, i, val);
  
    // Query 3: Query(L = 0, R = 3)
    queryPowerful(n, l, r);
  
  
    return 0;
}

chevron_right


Output:

Number of Powerful numbers between 0 to 3 = 2
Number of Powerful numbers between 0 to 3 = 3

Time Complexity: O(logN) per query

Attention reader! Don’t stop learning now. Get hold of all the important DSA concepts with the DSA Self Paced Course at a student-friendly price and become industry ready.




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.



Improved By : Akanksha_Rai