间隔总和和除数更新
给定一个包含 N 个整数的数组 A。您必须回答两种类型的查询:
1. 更新 [l, r] – 对于从 l 到 r 范围内的每个 i,用 D(A i ) 更新 A i ,其中 D(A i ) 表示 A i的除数数
2. 查询 [l, r] – 计算数组 A 中介于 l 和 r 之间的所有数字的总和。
输入以两个整数 N 和 Q 的形式给出,分别表示数组中的整数个数和查询的个数。下一行包含一个由 n 个整数组成的数组,后跟 Q 个查询,其中第 i 个查询表示为类型i 、 l i 、 r i 。
先决条件:二叉索引树 |段树
例子 :
Input : 7 4
6 4 1 10 3 2 4
2 1 7
2 4 5
1 3 5
2 4 4
Output : 30
13
4
解释:第一个查询是计算从 A 1到 A 7的数字之和,即 6 + 4
+ 1 + 10 + 3 + 2 + 4 = 30。同样,第二个查询结果为 13。对于第三个查询,
这是更新操作,因此 A 3将保持 1,A 4将变为 4,A 5将变为 2。
第四个查询将导致 A 4 = 4。
天真的方法:
一个简单的解决方案是运行从 l 到 r 的循环并计算给定范围内元素的总和。要更新一个值,请预先计算每个数字的除数的值,然后简单地执行 arr[i] = divisors[arr[i]]。
有效的方法:
这个想法是为了降低每个查询和更新操作的时间复杂度到 O(logN)。使用二叉索引树 (BIT) 或段树。构造一个 BIT[] 数组,具有查询和更新操作两个函数,并预先计算每个数字的除数。现在,对于每个更新操作,关键观察是数字“1”和“2”将分别以“1”和“2”作为它们的除数,所以如果它存在于更新查询的范围内,它们不会t 需要更新。我们将使用一个集合来存储仅大于 2 的数字的索引,并使用二分搜索查找更新查询的 l 索引并递增 l 索引,直到更新查询范围内的每个元素都更新。如果 arr[i] 只有 2 个除数,那么在更新它之后,将它从集合中删除,因为即使在任何下一次更新查询之后它也总是 2。对于求和查询操作,只需执行 query(r) – query(l – 1)。
// CPP program to calculate sum
// in an interval and update with
// number of divisors
#include
using namespace std;
int divisors[100], BIT[100];
// structure for queries with members type,
// leftIndex, rightIndex of the query
struct queries
{
int type, l, r;
};
// function to calculate the number
// of divisors of each number
void calcDivisors()
{
for (int i = 1; i < 100; i++) {
for (int j = i; j < 100; j += i) {
divisors[j]++;
}
}
}
// function for updating the value
void update(int x, int val, int n)
{
for (x; x <= n; x += x&-x) {
BIT[x] += val;
}
}
// function for calculating the required
// sum between two indexes
int sum(int x)
{
int s = 0;
for (x; x > 0; x -= x&-x) {
s += BIT[x];
}
return s;
}
// function to return answer to queries
void answerQueries(int arr[], queries que[], int n, int q)
{
// Declaring a Set
set s;
for (int i = 1; i < n; i++) {
// inserting indexes of those numbers
// which are greater than 2
if(arr[i] > 2) s.insert(i);
update(i, arr[i], n);
}
for (int i = 0; i < q; i++) {
// update query
if (que[i].type == 1) {
while (true) {
// find the left index of query in
// the set using binary search
auto it = s.lower_bound(que[i].l);
// if it crosses the right index of
// query or end of set, then break
if(it == s.end() || *it > que[i].r) break;
que[i].l = *it;
// update the value of arr[i] to
// its number of divisors
update(*it, divisors[arr[*it]] - arr[*it], n);
arr[*it] = divisors[arr[*it]];
// if updated value becomes less than or
// equal to 2 remove it from the set
if(arr[*it] <= 2) s.erase(*it);
// increment the index
que[i].l++;
}
}
// sum query
else {
cout << (sum(que[i].r) - sum(que[i].l - 1)) << endl;
}
}
}
// Driver Code
int main()
{
// precompute the number of divisors for each number
calcDivisors();
int q = 4;
// input array
int arr[] = {0, 6, 4, 1, 10, 3, 2, 4};
int n = sizeof(arr) / sizeof(arr[0]);
// declaring array of structure of type queries
queries que[q + 1];
que[0].type = 2, que[0].l = 1, que[0].r = 7;
que[1].type = 2, que[1].l = 4, que[1].r = 5;
que[2].type = 1, que[2].l = 3, que[2].r = 5;
que[3].type = 2, que[3].l = 4, que[3].r = 4;
// answer the Queries
answerQueries(arr, que, n, q);
return 0;
}
30
13
4
回答 Q 查询的时间复杂度为 O(Q * log(N))。