📜  查询子数组中的复合数字(带点更新)

📅  最后修改于: 2021-04-17 12:07:48             🧑  作者: Mango

给定一个由N个整数组成的数组,任务是对给定的数组执行以下两个操作:

例子

Input : arr = {1, 12, 3, 5, 17, 9}
        Query 1: query(start = 0, end = 4)
        Query 2: update(i = 3, x = 6)
        Query 3: query(start = 0, end = 4)
Output :1
        2
Explanation
In Query 1, the subarray [0...4]
has 1 Composite number viz. {12}

In Query 2, the value at index 3 
is updated to 6, the array arr now is, {1, 12, 3, 
6, 7, 9}
In Query 3, the subarray [0...4]
has 2 Composite Numbers viz. {12, 6}

由于我们需要处理范围查询和点更新,因此一种有效的方法是使用段树来解决该问题。段树最适合此目的。
我们可以使用Eratosthenes筛子对所有素数进行预处理,直到arr i可以取最大值,例如MAX。此操作的时间复杂度将为O(MAX log(log(MAX)))
构建段树:
可以使用段树将问题简化为子数组总和。
现在,我们可以构建分段树,其中叶节点表示为0(如果它是质数)或1(如果它是复合数)。
段树的内部节点等于其子节点的总和,因此,一个节点表示从L到R的范围内的总组合数,其中L到R的范围落在该节点下,而在它下面的子树也是如此。
处理查询和积分更新:
每当我们得到一个查询从开始到结束,那么我们就可以查询该段树节点的范围开始结束的总和,这又代表范围开始到结束复合材料的数量。
如果需要执行点更新并将索引i处的值更新为x,则我们检查以下情况:

下面是上述方法的实现:

C++
// C++ program to find number of composite numbers in a
// subarray and performing updates
 
#include 
using namespace std;
 
#define MAX 1000
 
// Function to calculate primes upto MAX
// using sieve of Eratosthenes
void sieveOfEratosthenes(bool isPrime[])
{
    isPrime[1] = true;
 
    for (int p = 2; p * p <= MAX; p++) {
 
        // If prime[p] is not changed, then
        // it is a prime
        if (isPrime[p] == true) {
 
            // Update all multiples of p
            for (int i = p * 2; i <= MAX; i += p)
                isPrime[i] = false;
        }
    }
}
 
// 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 to get the number of composites
    in a given range of array indexes. The following are
    parameters for this function.
 
    st --> Pointer to segment tree
    index --> 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]
    qs & qe --> Starting and ending indexes of
    query range
*/
int queryCompositesUtil(int* st, int ss, int se, int qs,
                        int qe, int index)
{
    // If segment of this node is a part of given range,
    // then return the number of composites
    // in the segment
    if (qs <= ss && qe >= se)
        return st[index];
 
    // If segment of this node is
    // outside the given range
    if (se < qs || ss > qe)
        return 0;
 
    // If a part of this segment
    // overlaps with the given range
    int mid = getMid(ss, se);
    return queryCompositesUtil(st, ss, mid, qs, qe, 2 * index + 1)
           + queryCompositesUtil(st, mid + 1, se, qs, qe, 2 * index + 2);
}
 
/*  A recursive function to update the nodes which
    have the given index in their range. The following
    are parameters st, si, ss and se are same as getSumUtil()
     
    i --> index of the element to be updated. This index is
          in input array.
    diff --> Value to be added to all nodes which
          have i in range
*/
void updateValueUtil(int* st, int ss, int se, int i,
                     int diff, int si)
{
    // Base Case: If the input index
    // lies outside the range of
    // this segment
    if (i < ss || i > se)
        return;
 
    // If the input index is in range of
    // this node, then update the value of
    // the node and its children
    st[si] = st[si] + diff;
 
    if (se != ss) {
        int mid = getMid(ss, se);
        updateValueUtil(st, ss, mid, i, diff, 2 * si + 1);
        updateValueUtil(st, mid + 1, se, i, diff, 2 * si + 2);
    }
}
 
// The function to update a value in input
// array and segment tree. It uses updateValueUtil()
// to update the value in segment tree
void updateValue(int arr[], int* st, int n, int i,
                 int new_val, bool isPrime[])
{
    // Check for erroneous input index
    if (i < 0 || i > n - 1) {
        printf("Invalid Input");
        return;
    }
 
    int diff, oldValue;
 
    oldValue = arr[i];
 
    // Update the value in array
    arr[i] = new_val;
 
    // Case 1: Old and new values both are primes
    if (isPrime[oldValue] && isPrime[new_val])
        return;
 
    // Case 2: Old and new values both composite
    if ((!isPrime[oldValue]) && (!isPrime[new_val]))
        return;
 
    // Case 3: Old value was composite, new value is prime
    if (!isPrime[oldValue] && isPrime[new_val]) {
        diff = -1;
    }
 
    // Case 4: Old value was prime, new_val is composite
    if (isPrime[oldValue] && !isPrime[new_val]) {
        diff = 1;
    }
 
    // Update the values of nodes in segment tree
    updateValueUtil(st, 0, n - 1, i, diff, 0);
}
 
// Return number of composite numbers in range
// from index qs (query start) to qe (query end).
// It mainly uses queryCompositesUtil()
void queryComposites(int* st, int n, int qs, int qe)
{
    int compositesInRange = queryCompositesUtil(st, 0, n - 1, qs, qe, 0);
 
    cout << "Number of Composites in subarray from " << qs
         << " to " << qe << " = " << compositesInRange << "\n";
}
 
// A recursive function that constructs Segment Tree
// for array[ss..se].
// si is index of current node in segment tree st
int constructSTUtil(int arr[], int ss, int se, int* st,
                    int si, bool isPrime[])
{
    // If there is one element in array, check if it
    // is prime then store 1 in the segment tree else
    // store 0 and return
    if (ss == se) {
 
        // if arr[ss] is composite
        if (!isPrime[arr[ss]])
            st[si] = 1;
        else
            st[si] = 0;
 
        return st[si];
    }
 
    // 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
    int mid = getMid(ss, se);
    st[si] = constructSTUtil(arr, ss, mid, st,
                             si * 2 + 1, isPrime)
             + constructSTUtil(arr, mid + 1, se, st,
                               si * 2 + 2, isPrime);
    return st[si];
}
 
/*  Function to construct segment tree from given array.
    This function allocates memory for segment tree and
    calls constructSTUtil() to fill the allocated memory */
int* constructST(int arr[], int n, bool isPrime[])
{
    // Allocate memory for segment tree
 
    // Height of segment tree
    int x = (int)(ceil(log2(n)));
 
    // Maximum size of segment tree
    int max_size = 2 * (int)pow(2, x) - 1;
 
    int* st = new int[max_size];
 
    // Fill the allocated memory st
    constructSTUtil(arr, 0, n - 1, st, 0, isPrime);
 
    // Return the constructed segment tree
    return st;
}
 
// Driver Code
int main()
{
 
    int arr[] = { 1, 12, 3, 5, 17, 9 };
    int n = sizeof(arr) / sizeof(arr[0]);
 
    /*  Preprocess all primes till MAX.
        Create a boolean array "isPrime[0..MAX]".
        A value in prime[i] will finally be false
        if i is composite, else true.
    */
    bool isPrime[MAX + 1];
    memset(isPrime, true, sizeof isPrime);
    sieveOfEratosthenes(isPrime);
 
    // Build segment tree from given array
    int* st = constructST(arr, n, isPrime);
 
    // Query 1: Query(start = 0, end = 4)
    int start = 0;
    int end = 4;
    queryComposites(st, n, start, end);
 
    // Query 2: Update(i = 3, x = 6), i.e Update
    // a[i] to x
    int i = 3;
    int x = 6;
    updateValue(arr, st, n, i, x, isPrime);
 
    // Query 3: Query(start = 0, end = 4)
    start = 0;
    end = 4;
    queryComposites(st, n, start, end);
 
    return 0;
}


Python3
# Python3 program to find
# number of composite numbers
# in a subarray and performing
# updates
import math
MAX = 1000
 
# Function to calculate primes
# upto MAX using sieve of Eratosthenes
def sieveOfEratosthenes(isPrime):
 
    isPrime[1] = True;
    p = 2
     
    while p * p <= MAX:
 
        # If prime[p] is not
        # changed, then
        # it is a prime
        if (isPrime[p] == True):
 
            # Update all multiples of p
            for i in range(p * 2,
                           MAX + 1,  p):
                isPrime[i] = False;
        p += 1
 
# A utility function to get
# the middle index from
# corner indexes.
def getMid(s, e):
 
    return s + (e - s) // 2;
 
'''  A recursive function to get the number
    of composites in a given range of array
    indexes. The following are parameters
    for this function.
 
    st --> Pointer to segment tree
    index --> 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]
    qs & qe --> Starting and ending indexes of
    query range
'''
 
def queryCompositesUtil(st, ss, se, qs,
                        qe, index):
 
    # If segment of this node is a
    # part of given range, then
    # return the number of composites
    # in the segment
    if (qs <= ss and qe >= se):
        return st[index];
 
    # If segment of this node is
    # outside the given range
    if (se < qs or ss > qe):
        return 0;
 
    # If a part of this segment
    # overlaps with the given range
    mid = getMid(ss, se);
    return (queryCompositesUtil(st, ss,
                                mid, qs,
                                qe, 2 * index + 1) +
            queryCompositesUtil(st, mid + 1,
                                se, qs, qe,
                                2 * index + 2));
 
'''  A recursive function to update the
     nodes which have the given index in
     their range. The following are parameters
     st, si, ss and se are same as getSumUtil()
 
     i --> index of the element to be updated.
           This index is in input array.
     diff --> Value to be added to all nodes
              which have i in range
'''
def updateValueUtil(st, ss, se, i,
                    diff, si):
 
    # Base Case: If the input index
    # lies outside the range of
    # this segment
    if (i < ss or i > se):
        return;
 
    # If the input index is in
    # range of this node, then
    # update the value of the
    # node and its children
    st[si] = st[si] + diff;
 
    if (se != ss):
        mid = getMid(ss, se);
        updateValueUtil(st, ss,
                        mid, i,
                        diff, 2 * si + 1);
        updateValueUtil(st, mid + 1,
                        se, i, diff,
                        2 * si + 2);
 
# The function to update a value
# in input array and segment tree.
# It uses updateValueUtil() to
# update the value in segment tree
def updateValue(arr,  st, n, i,
                new_val, isPrime):
 
    # Check for erroneous
    # input index
    if (i < 0 or i > n - 1):
        print("Invalid Input");
        return
 
    oldValue = arr[i];
 
    # Update the value in array
    arr[i] = new_val;
 
    # Case 1: Old and new values
    # both are primes
    if (isPrime[oldValue] and
        isPrime[new_val]):
        return;
 
    # Case 2: Old and new values
    # both composite
    if ((not isPrime[oldValue]) and
        (not isPrime[new_val])):
        return;
 
    # Case 3: Old value was composite,
    # new value is prime
    if (not isPrime[oldValue] and
        isPrime[new_val]):
        diff = -1;
 
    # Case 4: Old value was prime,
    # new_val is composite
    if (isPrime[oldValue] and
        not isPrime[new_val]):
        diff = 1;
 
    # Update the values of
    # nodes in segment tree
    updateValueUtil(st, 0,
                    n - 1, i,
                    diff, 0);
 
# Return number of composite
# numbers in range from index
# qs (query start) to qe (query end).
# It mainly uses queryCompositesUtil()
def queryComposites(st, n, qs, qe):
 
    compositesInRange = queryCompositesUtil(st, 0,
                                            n - 1,
                                            qs, qe, 0);
 
    print("Number of Composites in subarray from ",
          qs, " to ", qe, " = ", compositesInRange)
 
# A recursive function that constructs
# Segment Tree for array[ss..se].
# si is index of current node in
# segment tree st
def constructSTUtil(arr, ss, se, st,
                    si, isPrime):
 
    # If there is one element in array,
    # check if it is prime then store
    # 1 in the segment tree else store
    # 0 and return
    if (ss == se):
 
        # if arr[ss] is composite
        if (not isPrime[arr[ss]]):
            st[si] = 1;
        else:
            st[si] = 0;
 
        return st[si];
 
    # 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
    mid = getMid(ss, se);
    st[si] = (constructSTUtil(arr, ss,
                              mid, st,
                              si * 2 + 1,
                              isPrime) +
              constructSTUtil(arr, mid + 1,
                              se, st,
                              si * 2 + 2,
                              isPrime))
    return st[si];
 
'''  Function to construct segment tree
     from given array. This function
     allocates memory for segment tree
     and calls constructSTUtil() to fill
     the allocated memory '''
def constructST(arr, n, isPrime):
 
    # Allocate memory for
    # segment tree
 
    # Height of segment tree
    x = (int)(math.ceil(math.log2(n)));
 
    # Maximum size of segment tree
    max_size = 2 * pow(2, x) - 1;
 
    st = [0] * max_size
 
    # Fill the allocated memory st
    constructSTUtil(arr, 0, n - 1,
                    st, 0, isPrime);
 
    # Return the constructed
    # segment tree
    return st;
 
# Driver Code
if __name__ == "__main__":
 
    arr = [1, 12, 3, 5, 17, 9]
    n = len(arr)
 
    '''  Preprocess all primes till MAX.
        Create a boolean array "isPrime[0..MAX]".
        A value in prime[i] will finally be false
        if i is composite, else true.
    '''
    isPrime = [True] * (MAX + 1)
     
    sieveOfEratosthenes(isPrime);
 
    # Build segment tree from given array
    st = constructST(arr, n, isPrime);
 
    # Query 1: Query(start = 0,
    # end = 4)
    start = 0;
    end = 4;
    queryComposites(st, n,
                    start, end);
 
    # Query 2: Update(i = 3, x = 6),
    # i.e Update a[i] to x
    i = 3;
    x = 6;
    updateValue(arr, st, n, i,
                x, isPrime);
 
    # Query 3: Query(start = 0,
    # end = 4)
    start = 0;
    end = 4;
    queryComposites(st, n,
                    start, end)
 
# This code is contributed by Chitranayal


输出:
Number of Composites in subarray from 0 to 4 = 1
Number of Composites in subarray from 0 to 4 = 2



每个查询和更新的时间复杂度为O(logn),而构建段树的时间复杂度为O(n)
注意:在这里,使用Eratosthenes筛子进行预处理直至MAX的时间复杂度为O(MAX log(log(MAX))),其中MAX是arr i可以取的最大值。