【树状数组维护区间和】洛谷 P3372 【模板】线段树 1 【树状数组解法】
2021-08-04 16:18:00 # ACM

题链

这里用树状数组写法;

考虑求$[1,x]$的区间和 $sum_x$,于是将原数组差分后,相当于求一个二阶前缀和;

设原数组为 $a_i$, 差分后的数组为 $b_i = a_i - a_{i-1}$;

于是 $a_i = \sum_{j=1}^{i}{b_j}$;

那么[1,x]的区间和为 $sum_x = \sum_{i=1}^{x}a_i = \sum_{i=1}^{x} \sum_{j=1}^{i}b_j$

$sum_x = a_1 + a_2 + \cdots + a_x$

    $= (b_1) + (b_1+b_2) + (b_1+b_2+b_3) + \cdots + (b_1+b_2+b_3+\cdots+b_x)$

    $= xb_1 + (x-1)b_2 + (x-2)b_3 + \cdots + (x-n+1)b_n + \cdots + b_x $

    $= \sum_{n=1}^{x}{(x-n+1)b_n} $

    $= (x+1)\sum_{n=1}^{x}b_n - \sum_{n=1}^{x}b_n \cdot n $

于是分别在树状数组上维护 $\sum_{n=1}^{x}b_n $ 与 $ \sum_{n=1}^{x}b_n \cdot n $

$ ans = sum_r - sum_{l-1} $

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define MS 1000009

int n,m;
LL a[MS];
LL b[MS];
LL p[2][MS];

int lowbit(int x){
return x&(-x);
}

void add(int rt, int pos, LL val){
for(;pos<=n;pos+=lowbit(pos)) p[rt][pos] += val;
}

LL query(int pos){
LL cc = 0;
for(LL t=pos+1;pos;pos-=lowbit(pos)) cc += t*p[0][pos]-p[1][pos];
return cc;
}

int main(){
ios::sync_with_stdio(false);
cin >> n >> m;
for(int i=1;i<=n;i++){
cin >> a[i];
}
for(int i=1;i<=n;i++){
b[i] = a[i] - a[i-1];
add(0, i, b[i]);
add(1, i, b[i]*i);
}
while(m--){
LL op,l,r;
cin >> op >> l >> r;
if(op == 1){
LL x;
cin >> x;
add(0, l, x);
add(0, r+1, -x);
add(1, l, x*l);
add(1, r+1, -x*(r+1));
}
else if(op == 2){
LL ans = query(r) - query(l-1);
cout << ans << "\n";
}
}



return 0;
}


Prev
2021-08-04 16:18:00 # ACM
Next