推理过程
记$d[i]=a[i]-a[i-1]$,特别的,$d[1]=a[1]$,那么:
$\sum\limits_{j=1}^{i}a[j]=a[1]+a[2]+a[3]+\cdots+a[i]$
$=d[1]+(d[1]+d[2])+(d[1]+d[2]+d[3])+\cdots+(d[1]+d[2]+d[3]+\cdots+d[i])$
$=i*d[1]+(i-1)*d[2]+\cdots+2*d[i-1]+d[i]$
$=i*\sum\limits_{j=1}^{i}d[j]-(d[2]+2*d[3]+\cdots+(i-1)*d[i])$
$=(i+1)*\sum\limits_{j=1}^{i}d[j]-(d[1]+2*d[2]+3*d[3]+\cdots+i*d[i])$
$=(i+1)*\sum\limits_{j=1}^{i}d[j]-\sum\limits_{j=1}^{i}j*d[j]$
此时,$\sum\limits_{j=1}^{i}d[j]$可以在$O(logN)$时间复杂度内算出,令$f[j]=j*d[j]$,那么,$\sum\limits_{j=1}^{i}j*d[j]$也可以在$O(logN)$时间复杂度内算出,因此$\sum\limits_{j=1}^{i}a[j]$可在$O(logN)$时间复杂度内算出。
维护两个数组,$d[i] = a[i] - a[i - 1]$,$f[i] = i * d[i]$,剩下的,区间更新和区间查询就和【一维树状数组区间更新单点查询】一样了。
实现代码(以POJ3468为例)
#include<iostream> #include<cstdio> #include<cstring> using namespace std; #define MAXN 100010 typedef long long LL; int n, m; LL a[MAXN], d[MAXN], f[MAXN]; LL query(LL arr[], int x) { LL res = 0; for(int i = x; i > 0; i -= (i & -i))res += arr[i]; return res; } void update(LL arr[], int x, LL val) { for(int i = x; i <= n; i += (i & -i))arr[i] += val; } inline LL sum(int x) { return LL(x + 1) * query(d, x) - query(f, x); } int main() { while(cin >> n >> m) { for(int i = 1; i <= n; ++i) { scanf("%lld", a + i); LL t = a[i] - a[i - 1]; update(d, i, t); update(f, i, t * i); } char op[5]; int l, r, c; while(m--) { scanf("%s", op); if(op[0] == 'C') { scanf("%d%d%d", &l, &r, &c); update(d, l, c); update(d, r + 1, -c); update(f, l, 1LL * l * c); update(f, r + 1, -1LL * (r + 1)*c); } else { scanf("%d%d", &l, &r); printf("%lld\n", sum(r) - sum(l - 1)); } } } return 0; }