📜  间隔总和和除数更新

📅  最后修改于: 2022-05-13 01:57:05.110000             🧑  作者: Mango

间隔总和和除数更新

给定一个包含 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))。