给定一个由N个正整数组成的数组arr []和一个由{L,R}形式的Q个查询组成的数组Query [] [2] ,任务是从范围[L, R] ,除数为奇数。
例子:
Input: arr[] = {2, 4, 5, 6, 9}, Q = 3, Query[][] = {{0, 2}, {1, 3}, {1, 4}}
Output: 4 4 13
Explanation:
Query 1: Elements from indices [0, 2] are {2, 4, 5}. Out of them, only 4 has odd number of divisors. Therefore, the sum is 4.
Query 2: Elements from indices [1, 3] are {4, 5, 6}. Out of them, only 4 has odd number of divisors. Therefore, the sum is 4.
Query 3: Elements from the indices [1, 4] are {4, 5, 6, 9}. Out of them, only 4, 9 has odd number of divisors. Therefore, the sum is 13.
Input: arr[] = {1, 16, 5, 4, 9}, Q = 2, Query[][] = {{1, 3}, {0, 2}}
Output: 20 17
天真的方法:最简单的方法是解决给定的问题是遍历给定查询范围[L,R]内的给定数组arr []并找到[L,R]范围内具有奇数个元素的元素之和除数,然后打印结果总和。
时间复杂度: O(Q * N *√N)
辅助空间: O(1)
高效的方法:基于以下观察,还可以优化上述方法:
- 仅当平方数为正数时,除数的数目才为奇数。
- 因此,可以通过将没有奇数除数的整数替换为0来解决该问题。然后,构建一个分段树,以查找范围内的元素之和来回答查询。
请按照以下步骤解决问题:
- 遍历给定数组arr [],然后将非理想正方形的整数替换为0 。
- 建立一个细分树,以回答范围之间的总和查询。
- 遍历所有Q个查询,对于每个查询,从细分树中获取特定范围的总和。
下面是上述方法的实现:
C++
// C++ program for the above approach
#include
using namespace std;
// Function to get the middle index
// from the given ranges
int getMid(int s, int e)
{
return s + (e - s) / 2;
}
// Recursive function to find the sum
// of values in the given range of
// the array
int getSumUtil(int* st, int ss, int se,
int qs, int qe, int si)
{
// If segment of this node is a
// part of given range, then
// return the sum of the segment
if (qs <= ss && qe >= se)
return st[si];
// If segment of this node is
// outside the given range
if (se < qs || ss > qe)
return 0;
// If a part of this segment
// overlaps the given range
int mid = getMid(ss, se);
return getSumUtil(st, ss, mid,
qs, qe, 2 * si + 1)
+ getSumUtil(st, mid + 1,
se, qs, qe,
2 * si + 2);
}
// Function to find the sum of elements
// in the range from index qs (query
// start) to qe (query end)
int getSum(int* st, int n, int qs, int qe)
{
// Invalid ranges
if (qs < 0 || qe > n - 1 || qs > qe) {
cout << "Invalid Input";
return -1;
}
return getSumUtil(st, 0, n - 1, qs, qe, 0);
}
// Recursive function to construct the
// Segment Tree for array[ss..se]. si
// is index of current node in tree st
int constructSTUtil(int arr[], int ss,
int se, int* st,
int si)
{
// If there is one element
// in the array
if (ss == se) {
st[si] = arr[ss];
return arr[ss];
}
int mid = getMid(ss, se);
// Recur for left and right
// subtrees and store the sum
// of values in this node
st[si] = constructSTUtil(arr, ss, mid,
st, si * 2 + 1)
+ constructSTUtil(arr, mid + 1,
se, st,
si * 2 + 2);
return st[si];
}
// Function to construct segment tree
// from the given array
int* constructST(int arr[], int n)
{
// Allocate memory for the 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;
// Allocate memory
int* st = new int[max_size];
// Fill the allocated memory st
constructSTUtil(arr, 0, n - 1, st, 0);
// Return the constructed
// segment tree
return st;
}
// Function to find the sum of elements
// having odd number of divisors in
// index range [L, R] for Q queries
void OddDivisorsSum(int n, int q, int arr[],
vector > Query)
{
// Traverse the array, arr[]
for (int i = 0; i < n; i++) {
int sq = sqrt(arr[i]);
// Replace elements that are
// not perfect squares with 0
if (sq * sq != arr[i])
arr[i] = 0;
}
// Build segment tree from the
// given array
int* st = constructST(arr, n);
// Iterate through all the queries
for (int i = 0; i < q; i++) {
int l = Query[i].first;
int r = Query[i].second;
// Print sum of values in
// array from index l to r
cout << getSum(st, n, l, r) << " ";
}
}
// Driver Code
int main()
{
int arr[] = { 2, 4, 5, 6, 9 };
int N = sizeof(arr) / sizeof(arr[0]);
int Q = 3;
vector > Query
= { { 0, 2 }, { 1, 3 }, { 1, 4 } };
OddDivisorsSum(N, Q, arr, Query);
return 0;
}
C++
// C++ program for the above approach
#include
using namespace std;
// Function to find the sum of elements
// having odd number of divisors in
// index range [L, R] for Q queries
void OddDivisorsSum(int n, int q, int a[],
vector > Query)
{
// Initialize the dp[] array
int DP[n] = { 0 };
// Traverse the array, arr[]
for (int i = 0; i < n; i++) {
int x = sqrt(a[i]);
// If a[i] is a perfect square,
// then update value of DP[i] to a[i]
if (x * x == a[i])
DP[i] = a[i];
}
// Find the prefix sum of DP[] array
for (int i = 1; i < n; i++) {
DP[i] = DP[i - 1] + DP[i];
}
// Iterate through all the queries
for (int i = 0; i < q; i++) {
int l = Query[i].first;
int r = Query[i].second;
// Find the sum for each query
if (l == 0) {
cout << DP[r] << " ";
}
else {
cout << DP[r] - DP[l - 1]
<< " ";
}
}
}
// Driver Code
int main()
{
int arr[] = { 2, 4, 5, 6, 9 };
int N = sizeof(arr) / sizeof(arr[0]);
int Q = 3;
vector > Query
= { { 0, 2 }, { 1, 3 }, { 1, 4 } };
OddDivisorsSum(N, Q, arr, Query);
return 0;
}
4 4 13
时间复杂度: O(Q * log(N))
辅助空间: O(N)
高效方法:为了优化上述方法,其思想是使用辅助数组来存储具有奇数个除数的元素的前缀和。请按照以下步骤解决问题:
- 使用0初始化大小为N的数组dp [] 。
- 使用变量i遍历给定的数组arr []并检查是否有任何元素是理想的正方形。如果发现为真,则将dp [i]的值更新为arr [i] 。
- 找到数组dp []的前缀和。
- 遍历所有查询,对于[L,R]范围内的每个查询,答案将由(dp [R] – dp [L – 1])给出。
下面是上述方法的实现:
C++
// C++ program for the above approach
#include
using namespace std;
// Function to find the sum of elements
// having odd number of divisors in
// index range [L, R] for Q queries
void OddDivisorsSum(int n, int q, int a[],
vector > Query)
{
// Initialize the dp[] array
int DP[n] = { 0 };
// Traverse the array, arr[]
for (int i = 0; i < n; i++) {
int x = sqrt(a[i]);
// If a[i] is a perfect square,
// then update value of DP[i] to a[i]
if (x * x == a[i])
DP[i] = a[i];
}
// Find the prefix sum of DP[] array
for (int i = 1; i < n; i++) {
DP[i] = DP[i - 1] + DP[i];
}
// Iterate through all the queries
for (int i = 0; i < q; i++) {
int l = Query[i].first;
int r = Query[i].second;
// Find the sum for each query
if (l == 0) {
cout << DP[r] << " ";
}
else {
cout << DP[r] - DP[l - 1]
<< " ";
}
}
}
// Driver Code
int main()
{
int arr[] = { 2, 4, 5, 6, 9 };
int N = sizeof(arr) / sizeof(arr[0]);
int Q = 3;
vector > Query
= { { 0, 2 }, { 1, 3 }, { 1, 4 } };
OddDivisorsSum(N, Q, arr, Query);
return 0;
}
4 4 13
时间复杂度: O(N)
辅助空间: O(N)