题目描述
给定一个长度为N的数列A,以及M条指令,每条指令可能是以下两种之一:
1、“C l r d”,表示把 A[l],A[l+1],…,A[r] 都加上 d。
2、“Q l r”,表示询问 数列中第 l~r 个数的和。
对于每个询问,输出一个整数表示答案。
输入格式
第一行两个整数N,M。
第二行N个整数A[i]。
接下来M行表示M条指令,每条指令的格式如题目描述所示。
输出格式
对于每个询问,输出一个整数表示答案。
每个答案占一行。
数据范围
1≤N,M≤1051≤N,M≤105,
|d|≤10000|d|≤10000,
|A[i]|≤1000000000|A[i]|≤1000000000
输入样例:
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
题解:我们知道树状数组的基本用途是维护序列的前缀和以及单点更新。我们可以用b数组维护a数组的单点更新改变的值,a[i]加上b数组的前缀和就能得到a[i]更新后的值。那我们怎么算更新后的区间和呢?我们求数组a的前缀和a[1~x]相当于求,他可以写成:
那么我们可以再用一个c数组来维护i*b[i]的前缀和。问题就变成了求(sum[r]+(r+1)*query(b,r)-query(c,r))-(sum[l-1]+l*query(b,l-1)-query(c1,l-1))
代码:
#include <bits/stdc++.h> #define ll long long #define ull unsigned ll using namespace std; const int N = 1e5 + 10; const double eps = 1e-8; ll a[N],c[2][N],sum[N]; int lowbit(int x) {return x&(-x);} void add(int k,int x,int y) { while(x<N) { c[k][x]+=y; x += lowbit(x); } } ll query(int k,int x){ ll ans = 0; while(x) { ans += c[k][x]; x -= lowbit(x); } return ans; } int main() { int n,m,l,r,x; char s[5]; scanf("%d%d",&n,&m); for (int i = 1; i <= n; i++) { scanf("%lld",&a[i]); sum[i] = sum[i-1] + a[i]; } while(m--) { scanf("%s",s); if (s[0] == 'Q') { scanf("%d%d",&l,&r); ll ans = sum[r]+(r+1)*query(0,r)-query(1,r); ans -= sum[l-1]+l*query(1,l-1)-query(1,l-1); printf("%lld ",ans); }else if (s[0] == 'C') { scanf("%d%d%d",&l,&r,&x); add(0,l,x); add(0,r+1,-x); add(1,l,l*x); add(1,r+1,-(r+1)*x); } } return 0; }