📌  相关文章
📜  Q 查询的给定范围内 K 的可能余数计数

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

Q 查询的给定范围内 K 的可能余数计数

给定一个数组 arr[ ],其中包含 N 个正整数和一个整数 K 以及一个查询向量 Q 。可以询问两种类型的查询:

  • 在类型 1 查询中,从索引 l 到 r 的所有元素都增加值 X。此查询的输入格式:1 lrx
  • 在类型 2 查询中,对于给定的整数 K,如果从索引 l 到 r 的元素除以 k,则打印所有可能余数的计数。本次查询的输入格式:2 l r

例子:

天真的方法:一种简单的方法是在更新查询中迭代给定范围内的数组并更新值。对于另一种类型的查询,遍历给定范围并使用 k 取 mod,保留哈希数组以存储该余数的特定计数。但是这种方法的时间复杂度是 O(N*Q*k)

时间复杂度: O(N*Q*k)
辅助空间: O(k),用于维护大小为 k 的哈希数组

高效方法:上述方法可以使用分段树和惰性传播进行优化,基于以下思想:

直觉:

插图:

下面是上述方法的实现:

C++
// C++ program for Find count of all
// possible remainders for given  integer K
// in the given ranges for Q queries.
#include 
using namespace std;
#define MXX 100001
  
// to store propagate value
int lazy[4 * MXX];
int segment[4 * MXX][51];
int ans[51];
  
// build segment tree for given arr
void build(int arr[], int low, int high, int ind, int k)
{
    if (low == high) {
        int rem = arr[low] % k;
        // mark 1 at ind corresponding to remainder
        segment[ind][rem] = 1;
        return;
    }
  
    int mid = (low + high) >> 1;
    build(arr, low, mid, 2 * ind + 1, k);
    build(arr, mid + 1, high, 2 * ind + 2, k);
  
    // befor returning, compute answer for
    // all possible remainders for node ind
    for (int i = 0; i < k; i++) {
        segment[ind][i] = segment[2 * ind + 1][i]
                          + segment[2 * ind + 2][i];
    }
}
  
// to update a range l to r
void update(int l, int r, int low, int high, int ind, int k,
            int val)
{
    lazy[ind] %= k;
  
    // if any value is panding than update it
    if (lazy[ind] != 0) {
        if (low != high) {
            // propagate panding value to its children
            lazy[2 * ind + 1] += lazy[ind];
            lazy[2 * ind + 2] += lazy[ind];
            lazy[2 * ind + 1] %= k;
            lazy[2 * ind + 2] %= k;
        }
        int incr = lazy[ind];
        // make temporary vector to store value
        // so we can perform cycle operation without
        // loosing actual values
        vector temp(k);
  
        for (int i = 0; i < k; i++) {
            temp[i] = segment[ind][i];
        }
  
        // do cyclic shift operation
        for (int i = 0; i < k; i++) {
            segment[ind][(incr + i) % k] = temp[i];
        }
  
        // after done panding update mark it 0.
  
        lazy[ind] = 0;
    }
  
    // invalid range then return
    if (high < low || low > r || high < l)
        return;
  
    // the currant range is subset of
    // our actual range so update value
    if (low >= l && high <= r) {
  
        val %= k;
  
        vector temp(k);
  
        for (int i = 0; i < k; i++) {
            temp[i] = segment[ind][i];
        }
  
        for (int i = 0; i < k; i++) {
            segment[ind][(val + i) % k] = temp[i];
        }
        if (low != high) {
            lazy[2 * ind + 1] += val;
            lazy[2 * ind + 2] += val;
            lazy[2 * ind + 1] %= k;
            lazy[2 * ind + 2] %= k;
        }
  
        return;
    }
  
    int mid = (low + high) >> 1;
    // go to left and right side
    update(l, r, low, mid, 2 * ind + 1, k, val);
    update(l, r, mid + 1, high, 2 * ind + 2, k, val);
  
    // after updating and before returning,
    // calculate answer
    for (int i = 0; i < k; i++) {
        segment[ind][i] = segment[2 * ind + 1][i]
                          + segment[2 * ind + 2][i];
    }
}
  
// to compute answer of a query
// most of operation are same as update function
void query(int l, int r, int low, int high, int ind, int k)
{
    lazy[ind] %= k;
    if (lazy[ind] != 0) {
        if (low != high) {
            lazy[2 * ind + 1] += lazy[ind];
            lazy[2 * ind + 2] += lazy[ind];
            lazy[2 * ind + 1] %= k;
            lazy[2 * ind + 2] %= k;
        }
        int incr = lazy[ind];
        vector temp(k);
  
        for (int i = 0; i < k; i++) {
            temp[i] = segment[ind][i];
        }
  
        for (int i = 0; i < k; i++) {
            segment[ind][(incr + i) % k] = temp[i];
        }
  
        lazy[ind] = 0;
    }
  
    if (high < low || low > r || high < l)
        return;
  
    // this range is subset of our actual
    // require range so compute answer for
    // this range
    if (low >= l && high <= r) {
        for (int i = 0; i < k; i++)
            ans[i] += segment[ind][i];
        return;
    }
  
    int mid = (low + high) >> 1;
  
    query(l, r, low, mid, 2 * ind + 1, k);
    query(l, r, mid + 1, high, 2 * ind + 2, k);
}
  
// after printing answer
// reset ans array
void print(int k)
{
    for (int i = 0; i < k; i++)
        cout << ans[i] << " ";
    cout << "\n";
}
void reset()
{
    for (int i = 0; i < 51; i++)
        ans[i] = 0;
}
  
int main()
{
  
    int arr[] = { 7, 13, 5, 9, 16, 21 };
    int n = sizeof(arr) / sizeof(arr[0]);
    int q = 3, k = 4;
  
    // build segment tree
    build(arr, 0, n - 1, 0, k);
  
    // first query
    int x, l = 3, r = 5;
    query(l, r, 0, n - 1, 0, k);
    print(k);
    reset();
  
    // second query
    l = 0, r = 4, x = 1;
    update(l, r, 0, n - 1, 0, k, x);
  
    // third query
    l = 2, r = 5;
    query(l, r, 0, n - 1, 0, k);
    print(k);
    reset();
  
    return 0;
}


输出
1 2 0 0 
0 2 2 0 

时间复杂度:O(Q*K*logN)
辅助空间:O(N*K)