📌  相关文章
📜  范围查询以在给定数组上进行交替的加法和减法(1)

📅  最后修改于: 2023-12-03 15:41:26.364000             🧑  作者: Mango

范围查询以在给定数组上进行交替的加法和减法

介绍

在这个话题中,我们将讨论如何在给定的数组上执行交替的加法和减法,并使用区间查询算法计算范围查询。这个问题可以很容易地用线段树解决。

线段树

线段树是一种用于处理区间查询问题的数据结构。对于一个大小为n的数组,线段树可以在O(nlogn)的时间内进行建立,并能够对其进行快速查询和修改。线段树维护了一个树形结构,其中每个节点都代表了一个区间。具体地说,节点的左右儿子表示了该节点区间的左半部分和右半部分。

解决问题

要解决这个问题,我们需要在每个节点上存储以下信息:

  1. 该区间的和
  2. 该区间加法/减法操作的累积值

当我们遍历线段树并访问它的节点时,我们应该按照以下方式更新节点的值:

  1. 如果该节点没有叶子节点,则将其左右儿子的累积值相加,即为该节点的和。
  2. 否则,将左右儿子的和相加得到该节点的和,并根据该节点的累积值来更新它的左右儿子的累积值。

我们可以通过递归地访问这些节点来对线段树执行更新和查询操作。

代码示例
#include <iostream>

using namespace std;

const int MAX_N = 100005;

int n, m;
int x[MAX_N];
int add[MAX_N * 4], sub[MAX_N * 4], sum[MAX_N * 4];

void build(int c, int l, int r) {
    add[c] = sub[c] = 0;
    if (l == r) {
        sum[c] = x[l];
        return;
    }
    int mid = (l + r) / 2;
    build(c * 2, l, mid);
    build(c * 2 + 1, mid + 1, r);
    sum[c] = sum[c * 2] + sum[c * 2 + 1];
}

void push(int c, int len1, int len2) {
    int l = c * 2, r = c * 2 + 1;
    if (add[c] != 0) {
        add[l] += add[c];
        sub[l] += sub[c];
        sum[l] += add[c] * len1 - sub[c] * len1;
        add[r] += add[c];
        sub[r] += sub[c];
        sum[r] += add[c] * len2 - sub[c] * len2;
        add[c] = sub[c] = 0;
    }
}

void addt(int c, int p, int l, int r, int k) {
    if (l == r) {
        sum[c] += k;
        return;
    }
    int mid = (l + r) / 2, len1 = mid - l + 1, len2 = r - mid;
    push(c, len1, len2);
    if (p <= mid) addt(c * 2, p, l, mid, k);
    else addt(c * 2 + 1, p, mid + 1, r, k);
    sum[c] = sum[c * 2] + sum[c * 2 + 1];
}

void subt(int c, int p, int l, int r, int k) {
    if (l == r) {
        sum[c] -= k;
        return;
    }
    int mid = (l + r) / 2, len1 = mid - l + 1, len2 = r - mid;
    push(c, len1, len2);
    if (p <= mid) subt(c * 2, p, l, mid, k);
    else subt(c * 2 + 1, p, mid + 1, r, k);
    sum[c] = sum[c * 2] + sum[c * 2 + 1];
}

long long query(int c, int ql, int qr, int l, int r) {
    if (ql <= l && r <= qr) {
        return sum[c];
    }
    int mid = (l + r) / 2, len1 = mid - l + 1, len2 = r - mid;
    push(c, len1, len2);
    long long ans = 0;
    if (ql <= mid) ans += query(c * 2, ql, qr, l, mid);
    if (qr > mid) ans += query(c * 2 + 1, ql, qr, mid + 1, r);
    return ans;
}

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> x[i];
    }
    build(1, 1, n);
    for (int i = 1; i <= m; i++) {
        int op, l, r, k;
        cin >> op >> l >> r >> k;
        if (op == 1) {
            if (k % 2 == 1) {
                addt(1, l, 1, n, 1);
                subt(1, r + 1, 1, n, 1);
            } else {
                subt(1, l, 1, n, 1);
                addt(1, r + 1, 1, n, 1);
            }
        } else {
            if (k % 2 == 1) {
                cout << query(1, l, r, 1, n) << endl;
            } else {
                cout << -query(1, l, r, 1, n) << endl;
            }
        }
    }
    return 0;
}

以上是一个C++的实现。在这个示例中,我们使用两个数组add和sub来维护每个区间的累积增量和累积减量,并使用sum数组来存储每个节点对应的区间和。对于add和sum数组,我们递归地访问它们并在返回时进行更新;而对于sub数组,我们将它存储在递归过程中,并在访问节点时将它传递给左右儿子。可以看到,在处理交替加法和减法时,我们只是简单地在左右儿子的累积增量和累积减量中进行修改,而不是在区间和中修改。这确保了我们正确地执行了加法和减法操作,并保持了用于执行区间查询的值的正确性。

总结

本文演示了如何在给定的数组上执行交替的加法和减法,并使用区间查询算法计算范围查询。线段树是一种方便而强大的数据结构,可以用于解决广泛的问题。每个节点存储信息并在递归过程中进行更新是线段树算法的核心。