题面
输入样例
10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4
输出样例
4
55
9
15
题解
- 这道题涉及区间修改和区间查询,当然可以用线段树来做,但是杀鸡焉用宰牛刀,可以用树状数组解决的问题大可不必用线段树解决,如果对树状数组不了解的可以先看看这里,还是用一个简单的整数问题 那道题的思想,差分数组 (树状数组支持O(logn)单点修改,区间查询)
- “C l r d”,表示把 A[l],A[l+1],…,A[r] 都加上 d ,我们可以将其转化为差分数组,对于一段区间,我们只需要修改差分数组中的两个端点 b[l]+=c,b[r+1]-=c; 这样就可以实现区间修改了
- “Q l r”,表示询问 数列中第 l~r 个数的和。我们将原数组转化为差分数组以后,就无法进行区间查询了,因为差分数组求的前缀和是原数组的一个值,而不是原数组的区间和,那么怎样求原数组的区间和呢?
4.经过上边公式的推导,我们发现,只需要维护两个树状数组,一个是a[i] - a[i - 1] ,一个是i * (a[i] - a[i - 1]) ,这样就可以求出元素组的[1,x]的区间和,那么利用前缀和思想就可以求出原数组的任意区间,这样就完全转化为了树状数组的基本操作了
代码
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
ll n, m;
ll a[N]; //原数组
ll tr1[N]; //b[i]的前缀和数组
ll tr2[N]; //i*b[i]的前缀和数组
ll lowbit(ll x) {
return x & -x;
}
void add(ll tr[], ll x, ll c) {
for (ll i = x; i <= n; i += lowbit(i)) {
tr[i] += c;
}
}
ll sum(ll tr[], ll x) {
ll res = 0;
for (ll i = x; i; i -= lowbit(i)) {
res += tr[i];
}
return res;
}
ll prefix_sum(ll x) {
return sum(tr1, x) * (x + 1) - sum(tr2, x);
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
for (int i = 1; i <= n; i++) add(tr1, i, a[i] - a[i - 1]);
for (int i = 1; i <= n; i++) add(tr2, i, i * (a[i] - a[i - 1]));
while (m--) {
string op;
int l, r, d;
cin >> op >> l >> r;
if (op == "Q") {
cout << prefix_sum(r) - prefix_sum(l - 1) << endl;
} else {
cin >> d;
add(tr1, l, d), add(tr1, r + 1, -d);
add(tr2, l, l * d), add(tr2, r + 1, -(r + 1) * d);
}
}
return 0;
}