允许更新时计算给定数组范围内的原始数
给定一个由N个正整数组成的数组,以及Q个查询,其中每个查询是以下类型之一:
- 1 lr - 需要打印索引范围内的原始计数[l, r] 。
- 2 px - 需要在给定索引p处分配一个值x 。
例子:
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 indiced 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
朴素方法:基本方法是使用升序存储素数 筛法。然后对于第一种类型的每个查询,检查该范围内的所有元素并找到原始的计数,对于第二种类型的查询,将该值分配给给定的索引。
请按照以下步骤查找数字是否为原始数字:
- 从存储的第一个素数开始。
- 继续将这些存储的素数相乘。
- 如果素数的乘积在任何时候都等于 arr[i],则 arr[i] 是一个原始的。
- 如果乘积超过 arr[i],则 arr[i] 不是原始的。
请按照下图更好地了解如何找到原始
插图:
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 thean arr[i].
So arr[i] = 10 is not a primorial.
时间复杂度: O(Q * N)
辅助空间: O(1)
有效的方法:这个问题可以通过使用基于以下观察的段树在更短的时间内解决查询来更有效地解决:
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 segement 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.
按照下面提到的步骤来实施该方法:
- 使用埃拉托色尼筛法标记所有素数。
- 将原始值存储在地图中(例如primorial ) 。
- 为索引 0 到 N-1 的数组构建段树。
- 现在对于第一种类型的每个查询,在段树中找到该范围的值,对于第二种类型,使用给定值更新段树。
下面是上述方法的实现。
C++
// C++ code to count primorials
// in the given range for Q queries
#include
using namespace std;
const int mxx = 1e6 + 3;
vector isPrime(1000, true);
int segment[4 * mxx];
unordered_map 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);
// Upadte 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 completly 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 count(int arr[], int N,
vector >& queries)
{
vector 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 > queries{ { 1, 1, 4 },
{ 2, 2, 6 },
{ 1, 0, 3 } };
vector ans = count(arr, N, queries);
for (int x : ans)
cout << x << " ";
return 0;
}
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)
# Upadte 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 completly 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
2 3
时间复杂度: O(Q * log(N))
辅助空间: O(N)