Open In App

Count primorials in the given range of Array when updating is allowed

Last Updated : 31 Aug, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

Given an array consisting of N positive integers, and Q queries where each query is one of the following types:

  1. 1 l r – Requires printing the count of primorials in the range of indices [l, r].
  2. 2 p x –  Need to assign a value x at a given index p.

Examples:

Input: arr[] = {25, 2, 7, 30, 1}  queries = { {1, 1, 4 }, {2, 2, 6}, {1, 0, 3} }
Output:  2 3
Explanation: In the first query, find primorials from the index [1, 4 ]. 
So for indices in range [1, 4 ] only 2 and 30. These two elements are primorials because 2 
is the first prime number and 30= 2*3*5 (product of first three prime numbers). 
So the output is 2 for this query. 
In the second query, assign a value=6 at index 2. Now array will look like: { 25, 2, 6, 30, 1}. 
In the third query, there are 3 integers which are primorials in the range [0, 3] i.e.{2, 6, 30}. So the output is 3.

Input: arr[] = {210, 2, 6, 30, 2310}, queries = { {1, 2, 4}, {1, 0, 4}}
Output: 3 5
Explanation: All elements in the above array are primorial.
210  = 2*3*5*7
2310 = 2*3*5*7*11

 

Naive approach: The basic approach is to store prime numbers in ascending order using the sieve method. Then for each query of the first type check all the elements in that range and find the count of primorials and for 2nd type of query, assign that value to the given index.

Follow the steps below to find if a number is primorial or not: 

  • Start from the first prime number stored.
  • Keep multiplying these stored prime numbers.
  • If at any point product of primes becomes equal to arr[i], then arr[i] is a primorial.
  • If the product exceeds arr[i], then arr[i] is not a primorial. 

Follow the illustration below to have a better understanding of how to find a primorial

Illustration: 

To check arr[i]= 6 is primorial or not. initialize cur with first prime number.
cur = 2; 
cur = cur * 3, so now cur has value 6 and arr[ i ] = 6. So arr[ i ] is primorial.

To check arr[i]= 10, is primorial or not. Initialize cur with the first prime 2.
cur = 2;
cur = cur * 3, so now cur has value 6 
cur = cur * 5 = 6 * 5 = 30. 
Now cur has value 30 and  30 is greater than arr[i]. 
So arr[i] = 10 is not a primorial. 

Time Complexity: O(Q * N)
Auxiliary Space: O(1)

Efficient Approach: This problem can be solved more efficiently by solving the queries in less time using a segment tree based on the following observation:

Mark the primorials present in the array and store them in a data structure such that we can get the count of the primorials within a range of indices and update the values when needed in optimal time.
This task can be done by usage of the segment tree data structure.

Follow the below procedure to implement the segment tree data structure.

  • Store the first 15 primorials in ( because the 15th primorial is in order of 1017
  • Mark the array elements which are primorial.
  • In the segment tree, the leaf nodes store if the corresponding element of the array is primorial or not (stores 1 if primorial; otherwise stores 0) and the internal nodes store how many primorials are there in that given range (i.e. they store the sum of its child nodes).

The following is the segment tree for the given example arr[ ] = {25, 2, 7, 30, 1}:
(cnt represents the count of primorials corresponding to the range covered by that node )

 

For the query of type 2, if the assigned value x is primorial then store 1 corresponding to that leaf node otherwise store 0. Then update the internal nodes till the root of the segment tree. This will update the counts for the ranges.

Follow the steps mentioned below to implement the approach:

Below is the implementation of the above approach.

C++




// C++ code to count primorials
// in the given range for Q queries
 
#include <bits/stdc++.h>
using namespace std;
const int mxx = 1e6 + 3;
vector<bool> isPrime(1000, true);
int segment[4 * mxx];
unordered_map<long long, int> primorial;
 
// Mark true all prime numbers
void sieve()
{
    isPrime[0] = isPrime[1] = false;
    for (int i = 2; i < 1000; i++) {
        if (isPrime[i]) {
            for (int j = 2 * i; j < 1000;
                 j = j + i)
                // Mark false which are the
                // multiple of prime numbers
                isPrime[j] = false;
        }
    }
}
 
// Store primorials
void store_primorials()
{
    int cnt = 0;
    long long product = 1;
    for (int i = 2; i < mxx && cnt < 15;
         i++) {
        if (isPrime[i]) {
            product = product * 1LL * i;
            primorial[product]++;
            cnt++;
        }
    }
}
 
// To build segment tree
void build(int arr[], int low, int high,
           int ind)
{
    // The node is leaf node
    if (low == high) {
        // If array value is primorial
        // assign leaf node value 1
        // otherwise 0
        auto it = primorial.find(arr[low]);
        if (it != primorial.end())
            segment[ind] = 1;
        else
            segment[ind] = 0;
        return;
    }
 
    int mid = (low + high) >> 1;
    // Go to the left and right child of
    // current node
    build(arr, low, mid, 2 * ind + 1);
    build(arr, mid + 1, high, 2 * ind + 2);
 
    // Calculate count of primorials for
    // internal node of segment tree for
    // corresponding ranges
 
    segment[ind]
        = segment[2 * ind + 1]
          + segment[2 * ind + 2];
}
 
// To update segment tree nodes
void update(int low, int high, int ind,
            int pos, int val)
{
    if (low == high) {
        // If new assign value is primorial
        // then mark it 1 otherwise 0
        auto it = primorial.find(val);
        if (it != primorial.end())
            segment[ind] = 1;
        else
            segment[ind] = 0;
        return;
    }
 
    int mid = (low + high) >> 1;
 
    // Go to the left child if pos is on
    // the left side of current node
    if (pos >= low && pos <= mid)
        update(low, mid, 2 * ind + 1,
               pos, val);
    // Go to the right child if pos is on
    // the right side
    else
        update(mid + 1, high, 2 * ind + 2,
               pos, val);
 
    // Update all internal nodes of segment
    // tree
    segment[ind]
        = segment[2 * ind + 1]
          + segment[2 * ind + 2];
}
 
// To compute answer for given range l to r
int range_queries(int l, int r, int low,
                  int high, int ind)
{
    // [l, r] is given range, [low, high] is
    // current range if current range is
    // invalid return 0
    if (high < l || low > r)
        return 0;
 
    // If current range is completely inside of
    // the given range then return value of
    // that node
    if (low >= l && high <= r)
        return segment[ind];
 
    int mid = (low + high) >> 1;
    // Go to the left child and right child
    // to compute answer
    return (
        range_queries(l, r, low, mid,
                      2 * ind + 1)
        + range_queries(l, r, mid + 1, high,
                        2 * ind + 2));
}
 
// Function to find the count of primorials
vector<int> count(int arr[], int N,
                  vector<vector<int> >& queries)
{
    vector<int> ans;
 
    // Mark prime numbers as a true
    sieve();
    // To precompute primorials
    store_primorials();
 
    // Build segment tree for given array
    build(arr, 0, N - 1, 0);
 
    for (int i = 0; i < queries.size(); i++) {
        if (queries[i][0] == 1) {
            int l = queries[i][1];
            int r = queries[i][2];
            int x = range_queries(l, r, 0,
                                  N - 1, 0);
            ans.push_back(x);
        }
        else {
            update(0, N - 1, 0, queries[i][1],
                   queries[i][2]);
        }
    }
    return ans;
}
 
// Driver Code
int main()
{
    // Consider 0 based indexing
    int arr[] = { 25, 2, 7, 30, 1 };
    int N = sizeof(arr) / sizeof(arr[0]);
    vector<vector<int> > queries{ { 1, 1, 4 },
                                  { 2, 2, 6 },
                                  { 1, 0, 3 } };
 
    vector<int> ans = count(arr, N, queries);
    for (int x : ans)
        cout << x << " ";
    return 0;
}


Java




import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
class GFG {
    static final int mxx = 1000003;
    static boolean[] isPrime = new boolean[1000];
    static int[] segment = new int[4 * mxx];
    static Map<Long, Integer> primorial = new HashMap<>();
 
    // Mark true all prime numbers
    static void Sieve() {
        Arrays.fill(isPrime, true);
        isPrime[0] = isPrime[1] = false;
 
        for (int i = 2; i < 1000; i++) {
            if (isPrime[i]) {
                for (int j = 2 * i; j < 1000; j += i)
                    // Mark false which are the
                    // multiple of prime numbers
                    isPrime[j] = false;
            }
        }
    }
 
    // Store primorials
    static void StorePrimorials() {
        int cnt = 0;
        long product = 1;
        for (int i = 2; i < mxx && cnt < 15; i++) {
            if (isPrime[i]) {
                product *= (long) i;
                primorial.put(product, 1);
                cnt++;
            }
        }
    }
 
    // To build segment tree
    static void Build(int[] arr, int low, int high, int ind) {
        if (low == high) {
            // If array value is primorial
            // assign leaf node value 1
            // otherwise 0
            segment[ind] = primorial.containsKey((long) arr[low]) ? 1 : 0;
            return;
        }
 
        int mid = (low + high) >> 1;
        // Go to the left and right child of
        // the current node
        Build(arr, low, mid, 2 * ind + 1);
        Build(arr, mid + 1, high, 2 * ind + 2);
 
        // Calculate count of primorials for
        // internal node of segment tree for
        // corresponding ranges
        segment[ind] = segment[2 * ind + 1] + segment[2 * ind + 2];
    }
 
    // To update segment tree nodes
    static void Update(int low, int high, int ind, int pos, int val) {
        if (low == high) {
            // If new assign value is primorial
            // then mark it 1 otherwise 0
            segment[ind] = primorial.containsKey((long) val) ? 1 : 0;
            return;
        }
 
        int mid = (low + high) >> 1;
 
        // Go to the left child if pos is on
        // the left side of the current node
        if (pos >= low && pos <= mid)
            Update(low, mid, 2 * ind + 1, pos, val);
        // Go to the right child if pos is on
        // the right side
        else
            Update(mid + 1, high, 2 * ind + 2, pos, val);
 
        // Update all internal nodes of the segment
        // tree
        segment[ind] = segment[2 * ind + 1] + segment[2 * ind + 2];
    }
 
    // To compute the answer for the given range l to r
    static int RangeQueries(int l, int r, int low, int high, int ind) {
        if (high < l || low > r)
            return 0;
 
        if (low >= l && high <= r)
            return segment[ind];
 
        int mid = (low + high) >> 1;
        return (RangeQueries(l, r, low, mid, 2 * ind + 1)
                + RangeQueries(l, r, mid + 1, high, 2 * ind + 2));
    }
 
    // Function to find the count of primorials
    static List<Integer> Count(int[] arr, int N, List<List<Integer>> queries) {
        List<Integer> ans = new ArrayList<>();
 
        Sieve();
        StorePrimorials();
 
        Build(arr, 0, N - 1, 0);
 
        for (List<Integer> query : queries) {
            if (query.get(0) == 1) {
                int l = query.get(1);
                int r = query.get(2);
                int x = RangeQueries(l, r, 0, N - 1, 0);
                ans.add(x);
            } else {
                Update(0, N - 1, 0, query.get(1), query.get(2));
            }
        }
        return ans;
    }
     
      // Driver code
    public static void main(String[] args) {
        int[] arr = { 25, 2, 7, 30, 1 };
        int N = arr.length;
        List<List<Integer>> queries = new ArrayList<>();
        queries.add(Arrays.asList(1, 1, 4));
        queries.add(Arrays.asList(2, 2, 6));
        queries.add(Arrays.asList(1, 0, 3));
         
          // Function call
        List<Integer> ans = Count(arr, N, queries);
        for (int x : ans)
            System.out.print(x + " ");
    }
}
 
 
// by phasing17


Python3




# python3 code to count primorials
# in the given range for Q queries
mxx = int(1e6 + 3)
isPrime = [True for _ in range(1000)]
segment = [0 for _ in range(4 * mxx)]
primorial = {}
 
# Mark true all prime numbers
def sieve():
    global mxx, isprime, segment, primorial
    isPrime[0] = isPrime[1] = False
    for i in range(2, 1000):
        if (isPrime[i]):
            for j in range(2*i, 1000, i):
               
                # Mark false which are the
                # multiple of prime numbers
                isPrime[j] = False
 
# Store primorials
def store_primorials():
 
    global mxx, isprime, segment, primorial
    cnt = 0
    product = 1
    for i in range(2, mxx):
        if cnt >= 15:
            break
 
        if (isPrime[i]):
            product = product * i
            primorial[product] = primorial[product] + \
                1 if product in primorial else 1
            cnt += 1
 
# To build segment tree
def build(arr, low, high, ind):
    global mxx, isprime, segment, primorial
     
    # The node is leaf node
    if (low == high):
       
        # If array value is primorial
        # assign leaf node value 1
        # otherwise 0
 
        if (arr[low] in primorial):
            segment[ind] = 1
        else:
            segment[ind] = 0
        return
 
    mid = (low + high) >> 1
     
    # Go to the left and right child of
    # current node
    build(arr, low, mid, 2 * ind + 1)
    build(arr, mid + 1, high, 2 * ind + 2)
 
    # Calculate count of primorials for
    # internal node of segment tree for
    # corresponding ranges
    segment[ind] = segment[2 * ind + 1] + segment[2 * ind + 2]
 
# To update segment tree nodes
def update(low, high, ind, pos, val):
    global mxx, isprime, segment, primorial
    if (low == high):
       
        # If new assign value is primorial
        # then mark it 1 otherwise 0
        if (val in primorial):
            segment[ind] = 1
        else:
            segment[ind] = 0
        return
 
    mid = (low + high) >> 1
 
    # Go to the left child if pos is on
    # the left side of current node
    if (pos >= low and pos <= mid):
        update(low, mid, 2 * ind + 1,
               pos, val)
         
    # Go to the right child if pos is on
    # the right side
    else:
        update(mid + 1, high, 2 * ind + 2,
               pos, val)
 
    # Update all internal nodes of segment
    # tree
    segment[ind] = segment[2 * ind + 1] + segment[2 * ind + 2]
 
# To compute answer for given range l to r
def range_queries(l, r, low, high, ind):
    global mxx, isprime, segment, primorial
     
    # [l, r] is given range, [low, high] is
    # current range if current range is
    # invalid return 0
    if (high < l or low > r):
        return 0
 
    # If current range is completely inside of
    # the given range then return value of
    # that node
    if (low >= l and high <= r):
        return segment[ind]
 
    mid = (low + high) >> 1
     
    # Go to the left child and right child
    # to compute answer
    return (
        range_queries(l, r, low, mid,
                      2 * ind + 1)
        + range_queries(l, r, mid + 1, high,
                        2 * ind + 2))
 
# Function to find the count of primorials
def count(arr, N, queries):
    global mxx, isprime, segment, primorial
    ans = []
 
    # Mark prime numbers as a true
    sieve()
     
    # To precompute primorials
    store_primorials()
 
    # Build segment tree for given array
    build(arr, 0, N - 1, 0)
 
    for i in range(0, len(queries)):
        if (queries[i][0] == 1):
            l = queries[i][1]
            r = queries[i][2]
            x = range_queries(l, r, 0,
                              N - 1, 0)
            ans.append(x)
 
        else:
            update(0, N - 1, 0, queries[i][1],
                   queries[i][2])
 
    return ans
 
# Driver Code
if __name__ == "__main__":
 
    # Consider 0 based indexing
    arr = [25, 2, 7, 30, 1]
    N = len(arr)
    queries = [[1, 1, 4], [2, 2, 6], [1, 0, 3]]
 
    ans = count(arr, N, queries)
    for x in ans:
        print(x, end=" ")
 
    # This code is contributed by rakeshsahni


C#




// C# code to count primorials
// in the given range for Q queries
using System;
using System.Collections.Generic;
 
class GFG
{
    const int mxx = 1000003;
    static bool[] isPrime = new bool[1000];
    static int[] segment = new int[4 * mxx];
    static Dictionary<long, int> primorial = new Dictionary<long, int>();
 
    // Mark true all prime numbers
    static void Sieve()
    {
        for (int i = 0; i < 1000; i++)
            isPrime[i] = true;
 
        isPrime[0] = isPrime[1] = false;
 
        for (int i = 2; i < 1000; i++)
        {
            if (isPrime[i])
            {
                for (int j = 2 * i; j < 1000; j += i)
                    // Mark false which are the
                    // multiple of prime numbers
                    isPrime[j] = false;
            }
        }
    }
 
    // Store primorials
    static void StorePrimorials()
    {
        int cnt = 0;
        long product = 1;
        for (int i = 2; i < mxx && cnt < 15; i++)
        {
            if (isPrime[i])
            {
                product *= (long)i;
                primorial[product] = 1;
                cnt++;
            }
        }
    }
 
    // To build segment tree
    static void Build(int[] arr, int low, int high, int ind)
    {
        // The node is a leaf node
        if (low == high)
        {
            // If array value is primorial
            // assign leaf node value 1
            // otherwise 0
            segment[ind] = primorial.ContainsKey(arr[low]) ? 1 : 0;
            return;
        }
 
        int mid = (low + high) >> 1;
        // Go to the left and right child of
        // the current node
        Build(arr, low, mid, 2 * ind + 1);
        Build(arr, mid + 1, high, 2 * ind + 2);
 
        // Calculate count of primorials for
        // internal node of segment tree for
        // corresponding ranges
        segment[ind] = segment[2 * ind + 1] + segment[2 * ind + 2];
    }
 
    // To update segment tree nodes
    static void Update(int low, int high, int ind, int pos, int val)
    {
        if (low == high)
        {
            // If new assign value is primorial
            // then mark it 1 otherwise 0
            segment[ind] = primorial.ContainsKey(val) ? 1 : 0;
            return;
        }
 
        int mid = (low + high) >> 1;
 
        // Go to the left child if pos is on
        // the left side of the current node
        if (pos >= low && pos <= mid)
            Update(low, mid, 2 * ind + 1, pos, val);
        // Go to the right child if pos is on
        // the right side
        else
            Update(mid + 1, high, 2 * ind + 2, pos, val);
 
        // Update all internal nodes of the segment
        // tree
        segment[ind] = segment[2 * ind + 1] + segment[2 * ind + 2];
    }
 
    // To compute the answer for the given range l to r
    static int RangeQueries(int l, int r, int low, int high, int ind)
    {
        // [l, r] is given range, [low, high] is
        // current range if the current range is
        // invalid return 0
        if (high < l || low > r)
            return 0;
 
        // If the current range is completely inside of
        // the given range then return the value of
        // that node
        if (low >= l && high <= r)
            return segment[ind];
 
        int mid = (low + high) >> 1;
        // Go to the left child and right child
        // to compute the answer
        return (RangeQueries(l, r, low, mid, 2 * ind + 1)
                + RangeQueries(l, r, mid + 1, high, 2 * ind + 2));
    }
 
    // Function to find the count of primorials
    static List<int> Count(int[] arr, int N, List<List<int>> queries)
    {
        List<int> ans = new List<int>();
 
        // Mark prime numbers as true
        Sieve();
        // To precompute primorials
        StorePrimorials();
 
        // Build segment tree for the given array
        Build(arr, 0, N - 1, 0);
 
        foreach (var query in queries)
        {
            if (query[0] == 1)
            {
                int l = query[1];
                int r = query[2];
                int x = RangeQueries(l, r, 0, N - 1, 0);
                ans.Add(x);
            }
            else
            {
                Update(0, N - 1, 0, query[1], query[2]);
            }
        }
        return ans;
    }
 
    // Driver Code
    public static void Main(string[] args)
    {
        // Consider 0 based indexing
        int[] arr = { 25, 2, 7, 30, 1 };
        int N = arr.Length;
        List<List<int>> queries = new List<List<int>>
        {
            new List<int> { 1, 1, 4 },
            new List<int> { 2, 2, 6 },
            new List<int> { 1, 0, 3 }
        };
 
        List<int> ans = Count(arr, N, queries);
        foreach (int x in ans)
            Console.Write(x + " ");
    }
}


Javascript




<script>
// JavaScript code to count primorials
// in the given range for Q queries
 
let mxx = 1e6 + 3;
var isPrime = new Array(1000).fill(true);
var segment = new Array(4 * mxx).fill(0);
var primorial = {};
 
// Mark true all prime numbers
function sieve()
{
    isPrime[0] = false;
    isPrime[1] = false;
    for (var i = 2; i < 1000; i++) {
        if (isPrime[i]) {
            for (var j = 2 * i; j < 1000; j = j + i)
                // Mark false which are the
                // multiple of prime numbers
                isPrime[j] = false;
        }
    }
}
 
// Store primorials
function store_primorials()
{
    var cnt = 0;
    var product = 1;
    for (var i = 2; i < mxx && cnt < 15; i++) {
        if (isPrime[i]) {
            product = product * 1 * i;
            if (primorial.hasOwnProperty(product))
                primorial[product]++;
            else
                primorial[product] = 1;
            cnt++;
        }
    }
}
 
// To build segment tree
function build(arr, low, high, ind)
{
    // The node is leaf node
    if (low == high) {
        // If array value is primorial
        // assign leaf node value 1
        // otherwise 0
        if (primorial.hasOwnProperty(arr[low]))
            segment[ind] = 1;
        else
            segment[ind] = 0;
        return;
    }
 
    var mid = (low + high) >> 1;
    // Go to the left and right child of
    // current node
    build(arr, low, mid, 2 * ind + 1);
    build(arr, mid + 1, high, 2 * ind + 2);
 
    // Calculate count of primorials for
    // internal node of segment tree for
    // corresponding ranges
 
    segment[ind]
        = segment[2 * ind + 1] + segment[2 * ind + 2];
}
 
// To update segment tree nodes
function update(low, high, ind, pos, val)
{
    if (low == high) {
        // If new assign value is primorial
        // then mark it 1 otherwise 0
 
        if (primorial.hasOwnProperty(low))
            segment[ind] = 1;
        else
            segment[ind] = 0;
        return;
    }
 
    var mid = (low + high) >> 1;
 
    // Go to the left child if pos is on
    // the left side of current node
    if (pos >= low && pos <= mid)
        update(low, mid, 2 * ind + 1, pos, val);
    // Go to the right child if pos is on
    // the right side
    else
        update(mid + 1, high, 2 * ind + 2, pos, val);
 
    // Update all internal nodes of segment
    // tree
    segment[ind]
        = segment[2 * ind + 1] + segment[2 * ind + 2];
}
 
// To compute answer for given range l to r
function range_queries(l, r, low, high, ind)
{
    // [l, r] is given range, [low, high] is
    // current range if current range is
    // invalid return 0
    if (high < l || low > r)
        return 0;
 
    // If current range is completely inside of
    // the given range then return value of
    // that node
    if (low >= l && high <= r)
        return segment[ind];
 
    var mid = (low + high) >> 1;
    // Go to the left child and right child
    // to compute answer
    return (
        range_queries(l, r, low, mid, 2 * ind + 1)
        + range_queries(l, r, mid + 1, high, 2 * ind + 2));
}
 
// Function to find the count of primorials
function count(arr, N, queries)
{
    var ans = [];
 
    // Mark prime numbers as a true
    sieve();
    // To precompute primorials
    store_primorials();
 
    // Build segment tree for given array
    build(arr, 0, N - 1, 0);
 
    for (var i = 0; i < queries.length; i++) {
        if (queries[i][0] == 1) {
            var l = queries[i][1];
            var r = queries[i][2];
            var x = range_queries(l, r, 0, N - 1, 0);
            ans.push(x);
        }
        else {
            update(0, N - 1, 0, queries[i][1],
                   queries[i][2]);
        }
    }
    return ans;
}
 
// Driver Code
 
// Consider 0 based indexing
var arr = [ 25, 2, 7, 30, 1 ];
var N = arr.length;
var queries = [ [ 1, 1, 4 ], [ 2, 2, 6 ], [ 1, 0, 3 ] ];
 
var ans = count(arr, N, queries);
for (var i = 0; i < ans.length; i++)
    document.write(ans[i] + " ");
     
// This code is contributed by phasing17
</script>


Output

2 3 




Time Complexity: O(Q * log(N))
Auxiliary Space: O(N)



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads