给定一个由N个整数组成的数组,任务是对给定的数组执行以下两个操作:
query(start, end) : Print the number of Composite numbers in the subarray from start to end
update(i, x) : update the value at index i to x, i.e arr[i] = x
例子:
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,则我们检查以下情况:
Let the old value of arri be y and the new value be x.
Case 1: If x and y both are composites.
Count of composites in the subarray does not change, so we just update array and donot
modify the segment tree
Case 2: If x and y both are primes.
Count of composites in the subarray does not change, so we just update array and donot
modify the segment tree
Case 3: If y is composite but x is prime.
Count of composite numbers in the subarray decreases, so we update array and add -1 to every
range, the index i which is to be updated, is a part of in the segment tree
Case 4: If y is prime but x is composite.
Count of composite numbers in the subarray increases, so we update array and add 1 to every
range, the index i which is to be updated, is a part of in the segment tree
下面是上述方法的实现:
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可以取的最大值。