给定大小为N的数组 arr和形式为[L, R] 的Q查询,任务是在给定范围内找到该数组的乘积的除数数。
注意:范围为 1 位。
例子:
Input: arr[] = {4, 1, 9, 12, 5, 3}, Q = {{1, 3}, {3, 5}}
Output: 9
24
Input: arr[] = {5, 2, 3, 1, 4}, Q = {{2, 4}, {1, 5}}
Output: 4
16
蛮力:
- 对于每个查询,从 L 到 R 迭代数组并计算给定范围内元素的乘积。
- 计算该乘积的除数总数。
时间复杂度: Q * N * (log (N))
Efficient Approach:思路是使用Segment Tree来解决这个问题。
- 由于限制,我们无法存储从 L 到 R 的数组的乘积,因此我们可以将素数的幂存储在段树中。
- 对于每个查询,我们根据查询合并树。
从给定数组构建段树
- 我们从一个段 arr[0 开始。 . . n-1]。
- 每次我们将当前段分成两半,如果它还没有变成长度为 1 的段。
- 然后在两半上调用相同的过程,对于每个这样的段,我们将质数的幂存储在相应的节点中。
- 除最后一层外,构建的线段树的所有层都将被完全填充。
- 此外,这棵树将是一个完整的二叉树,因为我们总是在每一层将段分成两半。
- 由于构造的树总是一棵有 n 个叶子的全二叉树,因此会有 n-1 个内部节点。所以节点总数将为2*n – 1 。
下面是上述方法的实现。
C++
#include
using namespace std;
#define ll long long int
#define MOD 1000000007
#define MAXN 1000001
// Array to store the precomputed prime numbers
int spf[MAXN];
// Function signatures
void merge(
vector > tree[], int treeIndex);
void sieve();
vector getFactorization(
int x);
void buildTree(
vector > tree[], int treeIndex,
int start, int end, int arr[]);
int query(
vector > tree[], int treeIndex,
int start, int end,
int left, int right);
vector > mergeUtil(
vector > op1,
vector > op2);
vector > queryUtil(
vector > tree[], int treeIndex,
int start, int end,
int left, int right);
// Function to use Sieve to compute
// and store prime numbers
void sieve()
{
spf[1] = 1;
for (int i = 2; i < MAXN; i++)
spf[i] = i;
for (int i = 4; i < MAXN; i += 2)
spf[i] = 2;
for (int i = 3; i * i < MAXN; i++) {
if (spf[i] == i) {
for (int j = i * i; j < MAXN; j += i)
if (spf[j] == j)
spf[j] = i;
}
}
}
// Function to find the
// prime factors of a value
vector getFactorization(int x)
{
// Vectore to store the prime factors
vector ret;
// For every prime factor of x,
// push it to the vector
while (x != 1) {
ret.push_back(spf[x]);
x = x / spf[x];
}
// Return the prime factors
return ret;
}
// Recursive function to build segment tree
// by merging the power pf primes
void buildTree(vector > tree[],
int treeIndex, int start,
int end, int arr[])
{
// Base case
if (start == end) {
int val = arr[start];
vector primeFac
= getFactorization(val);
for (auto it : primeFac) {
int power = 0;
while (val % it == 0) {
val = val / it;
power++;
}
// Storing powers of prime
// in tree at treeIndex
tree[treeIndex]
.push_back({ it, power });
}
return;
}
int mid = (start + end) / 2;
// Building the left tree
buildTree(tree, 2 * treeIndex, start,
mid, arr);
// Building the right tree
buildTree(tree, 2 * treeIndex + 1,
mid + 1, end, arr);
// Merges the right tree and left tree
merge(tree, treeIndex);
return;
}
// Function to merge the right
// and the left tree
void merge(vector > tree[],
int treeIndex)
{
int index1 = 0;
int index2 = 0;
int len1 = tree[2 * treeIndex].size();
int len2 = tree[2 * treeIndex + 1].size();
// Fill this array in such a way such that values
// of prime remain sorted similar to mergesort
while (index1 < len1 && index2 < len2) {
// If both of the elements are same
if (tree[2 * treeIndex][index1].first
== tree[2 * treeIndex + 1][index2].first) {
tree[treeIndex].push_back(
{ tree[2 * treeIndex][index1].first,
tree[2 * treeIndex][index1].second
+ tree[2 * treeIndex + 1]
[index2]
.second });
index1++;
index2++;
}
// If the element on the left part
// is greater than the right part
else if (tree[2 * treeIndex][index1].first
> tree[2 * treeIndex + 1][index2].first) {
tree[treeIndex].push_back(
{ tree[2 * treeIndex + 1][index2].first,
tree[2 * treeIndex + 1][index2].second });
index2++;
}
// If the element on the left part
// is less than the right part
else {
tree[treeIndex].push_back(
{ tree[2 * treeIndex][index1].first,
tree[2 * treeIndex][index1].second });
index1++;
}
}
// Insert the leftover elements
// from the left part
while (index1 < len1) {
tree[treeIndex].push_back(
{ tree[2 * treeIndex][index1].first,
tree[2 * treeIndex][index1].second });
index1++;
}
// Insert the leftover elements
// from the right part
while (index2 < len2) {
tree[treeIndex].push_back(
{ tree[2 * treeIndex + 1][index2].first,
tree[2 * treeIndex + 1][index2].second });
index2++;
}
return;
}
// Function similar to merge() method
// But here it takes two vectors and
// return a vector of power of primes
vector > mergeUtil(
vector > op1,
vector > op2)
{
int index1 = 0;
int index2 = 0;
int len1 = op1.size();
int len2 = op2.size();
vector > res;
while (index1 < len1 && index2 < len2) {
if (op1[index1].first == op2[index2].first) {
res.push_back({ op1[index1].first,
op1[index1].second
+ op2[index2].second });
index1++;
index2++;
}
else if (op1[index1].first > op2[index2].first) {
res.push_back({ op2[index2].first,
op2[index2].second });
index2++;
}
else {
res.push_back({ op1[index1].first,
op1[index1].second });
index1++;
}
}
while (index1 < len1) {
res.push_back({ op1[index1].first,
op1[index1].second });
index1++;
}
while (index2 < len2) {
res.push_back({ op2[index2].first,
op2[index2].second });
index2++;
}
return res;
}
// Function similar to query() method
// as in segment tree
// Here also the result is merged
vector > queryUtil(
vector > tree[],
int treeIndex, int start,
int end, int left, int right)
{
if (start > right || end < left) {
tree[0].clear();
return tree[0];
}
if (start >= left && end <= right) {
return tree[treeIndex];
}
int mid = (start + end) / 2;
vector > op1
= queryUtil(tree, 2 * treeIndex,
start, mid,
left, right);
vector > op2
= queryUtil(tree, 2 * treeIndex + 1,
mid + 1, end,
left, right);
return mergeUtil(op1, op2);
}
// Function to return the number of divisors
// of product of array from left to right
int query(vector > tree[],
int treeIndex, int start,
int end, int left, int right)
{
vector > res
= queryUtil(tree, treeIndex,
start, end,
left, right);
int sum = 1;
for (auto it : res) {
sum *= (it.second + 1);
sum %= MOD;
}
return sum;
}
// Function to solve queries
void solveQueries(
int arr[], int len,
int l, int r)
{
// Calculate the size of segment tree
int x = (int)(ceil(log2(len)));
// Maximum size of segment tree
int max_size = 2 * (int)pow(2, x) - 1;
// first of pair is used to store the prime
// second of pair is use to store its power
vector > tree[max_size];
// building the tree
buildTree(tree, 1, 0, len - 1, arr);
// Find the required number of divisors
// of product of array from l to r
cout << query(tree, 1, 0,
len - 1, l - 1, r - 1)
<< endl;
}
// Driver code
int main()
{
// Precomputing the prime numbers using sieve
sieve();
int arr[] = { 5, 2, 3, 1, 4 };
int len = sizeof(arr) / sizeof(arr[0]);
int queries = 2;
int Q[queries][2] = { { 2, 4 }, { 1, 5 } };
// Solve the queries
for (int i = 0; i < queries; i++) {
solveQueries(arr, len, Q[i][0], Q[i][1]);
}
return 0;
}
Python3
from typing import List, Tuple
from math import ceil, log2, pow
import sys
sys.setrecursionlimit(10000)
MOD = 1000000007
MAXN = 1000001
# Array to store the precomputed prime numbers
spf = [0 for _ in range(MAXN)]
# Function to use Sieve to compute
# and store prime numbers
def sieve() -> None:
spf[1] = 1
for i in range(2, MAXN):
spf[i] = i
for i in range(4, MAXN, 2):
spf[i] = 2
i = 3
while i * i < MAXN:
if (spf[i] == i):
for j in range(i * i, MAXN, i):
if (spf[j] == j):
spf[j] = i
i += 1
# Function to find the
# prime factors of a value
def getFactorization(x: int) -> List[int]:
# Vectore to store the prime factors
ret = []
# For every prime factor of x,
# push it to the vector
while (x != 1):
ret.append(spf[x])
x = x // spf[x]
# Return the prime factors
return ret
# Recursive function to build segment tree
# by merging the power pf primes
def buildTree(tree: List[List[Tuple[int]]], treeIndex: int,
start: int, end: int, arr: List[int]) -> None:
# Base case
if (start == end):
val = arr[start]
primeFac = getFactorization(val)
for it in primeFac:
power = 0
while (val % it == 0):
val = val // it
power += 1
# Storing powers of prime
# in tree at treeIndex
tree[treeIndex].append((it, power))
return
mid = (start + end) // 2
# Building the left tree
buildTree(tree, 2 * treeIndex, start, mid, arr)
# Building the right tree
buildTree(tree, 2 * treeIndex + 1, mid + 1, end, arr)
# Merges the right tree and left tree
merge(tree, treeIndex)
return
# Function to merge the right
# and the left tree
def merge(tree: List[List[Tuple[int]]], treeIndex: int) -> None:
index1 = 0
index2 = 0
len1 = len(tree[2 * treeIndex])
len2 = len(tree[2 * treeIndex + 1])
# Fill this array in such a way such that values
# of prime remain sorted similar to mergesort
while (index1 < len1 and index2 < len2):
# If both of the elements are same
if (tree[2 * treeIndex][index1][0] ==
tree[2 * treeIndex + 1][index2][0]):
tree[treeIndex].append((tree[2 * treeIndex][index1][0],
tree[2 * treeIndex][index1][1] +
tree[2 * treeIndex + 1][index2][1]))
index1 += 1
index2 += 1
# If the element on the left part
# is greater than the right part
elif (tree[2 * treeIndex][index1][0] >
tree[2 * treeIndex + 1][index2][0]):
tree[treeIndex].append((tree[2 * treeIndex + 1][index2][0],
tree[2 * treeIndex + 1][index2][1]))
index2 += 1
# If the element on the left part
# is less than the right part
else:
tree[treeIndex].append((tree[2 * treeIndex][index1][0],
tree[2 * treeIndex][index1][1]))
index1 += 1
# Insert the leftover elements
# from the left part
while (index1 < len1):
tree[treeIndex].append(
(tree[2 * treeIndex][index1][0],
tree[2 * treeIndex][index1][1]))
index1 += 1
# Insert the leftover elements
# from the right part
while (index2 < len2):
tree[treeIndex].append((tree[2 * treeIndex + 1][index2][0],
tree[2 * treeIndex + 1][index2][1]))
index2 += 1
return
# Function similar to merge() method
# But here it takes two vectors and
# return a vector of power of primes
def mergeUtil(op1: List[Tuple[int]],
op2: List[Tuple[int]]) -> List[Tuple[int]]:
index1 = 0
index2 = 0
len1 = len(op1)
len2 = len(op2)
res = []
while (index1 < len1 and index2 < len2):
if (op1[index1][0] == op2[index2][0]):
res.append((op1[index1][0],
(op1[index1][1] +
op2[index2][1])))
index1 += 1
index2 += 1
elif (op1[index1][0] > op2[index2][0]):
res.append((op2[index2][0], op2[index2][1]))
index2 += 1
else:
res.append((op1[index1][0], op1[index1][1]))
index1 += 1
while (index1 < len1):
res.append((op1[index1][0], op1[index1][1]))
index1 += 1
while (index2 < len2):
res.append((op2[index2][0], op2[index2][1]))
index2 += 1
return res
# Function similar to query() method
# as in segment tree
# Here also the result is merged
def queryUtil(tree: List[List[Tuple[int]]], treeIndex: int,
start: int,end: int, left: int,
right: int) -> List[Tuple[int]]:
if (start > right or end < left):
tree[0].clear()
return tree[0]
if (start >= left and end <= right):
return tree[treeIndex]
mid = (start + end) // 2
op1 = queryUtil(tree, 2 * treeIndex, start,
mid, left, right)
op2 = queryUtil(tree, 2 * treeIndex + 1, mid + 1,
end, left, right)
return mergeUtil(op1, op2)
# Function to return the number of divisors
# of product of array from left to right
def query(tree: List[List[Tuple[int]]], treeIndex: int,
start: int, end: int, left: int, right: int) -> int:
res = queryUtil(tree, treeIndex, start, end, left, right)
sum = 1
for it in res:
sum *= (it[1] + 1)
sum %= MOD
return sum
# Function to solve queries
def solveQueries(arr: List[int], length: int,
l: int, r: int) -> None:
# Calculate the size of segment tree
x = int(ceil(log2(length)))
# Maximum size of segment tree
max_size = 2 * int(pow(2, x)) - 1
# First of pair is used to store the prime
# second of pair is use to store its power
tree = [[] for _ in range(max_size)]
# building the tree
buildTree(tree, 1, 0, length - 1, arr)
# Find the required number of divisors
# of product of array from l to r
print(query(tree, 1, 0, length - 1, l - 1, r - 1))
# Driver code
if __name__ == "__main__":
# Precomputing the prime numbers using sieve
sieve()
arr = [ 5, 2, 3, 1, 4 ]
length = len(arr)
queries = 2
Q = [ [ 2, 4 ], [ 1, 5 ] ]
# Solve the queries
for i in range(queries):
solveQueries(arr, length, Q[i][0], Q[i][1])
# This code is contributed by sanjeev2552
输出:
4
16
时间复杂度: Q * (log (N))
如果您想与行业专家一起参加直播课程,请参阅Geeks Classes Live