分析
-
对于最原始的树状数组存在两个操作:单点加,求区间和(即a[x]+=c, query[L~R]);
-
本题中的操作正好反过来:区间加,求单点和(即a[L~R]+=c, query[x]);
-
这一题的做法是使用差分的思想,将原数组a转化成差分数组b,则:
(1)$a[L,R]+=c \iff b[L]+=c, b[R+1]-=c$;
(2)$a[x] = \sum b[i], 1 \le i \le x$;
- 如何将原数组a转换为差分数组呢?转化过程如下,这里必须要求数据从a[1]开始,a[0]=0:
$$ b[1] = a[1] - a[0] \\\\ b[2] = a[2] - a[1] \\\\ … \\\\ b[n] = a[n] - a[n - 1] $$
-
这样,对于数组a的区间加法可以转化成对数组b的单点加;对数组a求单点和可以转化成对数组b的区间和,就可以使用树状数组的操作了。
-
另外数组a中的数据最大为$10^9$,操作次数为$10^5$,每次最大加上1000,不会超过int的范围。
#include <iostream>
#include <cstring>
using namespace std;
const int N = 100010;
int n, m; // 数列长度、操作个数
int a[N]; // 原数组
int tr[N];
int lowbit(int x) {
return x & -x;
}
// 操作原数组,让b[x] += c;
// 这里的b是差分数组
void add(int x, int c) {
for (int i = x; i <= n; i += lowbit(i)) tr[i] += c;
}
// 查询,获得b[1...x]的区间和
int sum(int x) {
int res = 0;
for (int i = x; i; i -= lowbit(i)) res += tr[i];
return res;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n; i++) add(i, a[i] - a[i - 1]); // 使用差分数组初始化
while (m--) {
char op[2];
int l, r, d;
scanf("%s%d", op, &l);
if (*op == 'C') {
scanf("%d%d", &r, &d);
add(l, d), add(r + 1, -d);
} else {
printf("%d\n", sum(l));
}
}
return 0;
}