一维树状数组
1.1 单点修改 + 区间和
【任务】修改数组中某个元素,求
[
l
,
r
]
[l,r]
[l,r] 区间和。
【说明】原始数组
a
[
i
]
a[i]
a[i], 树状数组
c
[
i
]
c[i]
c[i] 存储
a
[
i
]
a[i]
a[i].
【接口】
          
\;\;\;\;\;
void update(int i, int x);
          
\;\;\;\;\;
复杂度:
O
(
l
o
g
n
)
O(logn)
O(logn)
          
\;\;\;\;\;
功能:单点修改
a
[
i
]
+
x
a[i] + x
a[i]+x.
          
\;\;\;\;\;
调用:update(i, x).
          
\;\;\;\;\;
int Query_sum(int i);
          
\;\;\;\;\;
复杂度:
O
(
l
o
g
n
)
O(logn)
O(logn)
          
\;\;\;\;\;
功能:返回前缀和
s
u
m
[
i
]
=
∑
k
=
1
i
a
[
k
]
sum[i] = \sum_{k=1}^{i} a[k]
sum[i]=∑k=1ia[k].
          
\;\;\;\;\;
调用:sum[ l, r ] = Query_sum( r ) - Query_sum( l - 1 ).
【代码】
void update(int i, int x) {
for(int k = i; k <= n; k += lowbit(k)) { // 单点修改 a[i] + x
c[k] += x;
}
}
int Query_sum(int i) {
int ans = 0;
for(int k = i; k >= 1; k -= lowbit(k)) { // 前缀和 sum[i]
ans += c[k];
}
return ans;
}
1.2 区间修改 + 单点查询
【任务】将区间
[
l
,
r
]
[l,r]
[l,r] 中每个数
+
x
+x
+x , 查询数组中某个元素的值。
【说明】原始数组
a
[
i
]
a[i]
a[i], 树状数组
c
[
i
]
c[i]
c[i] 存储
d
[
i
]
d[i]
d[i],
d
[
i
]
=
a
[
i
]
−
a
[
i
−
1
]
d[i] = a[i] - a[i-1]
d[i]=a[i]−a[i−1].
          
\;\;\;\;\;
区间修改可以转化为单点修改:对区间
[
l
,
r
]
[l,r]
[l,r] 中每个数
+
x
+x
+x 之后,对于
d
[
i
]
d[i]
d[i] 数组,区间
[
l
+
1
,
r
]
[l+1,r]
[l+1,r] 保持不变,只有两个地方需要改
d
[
l
]
+
x
,
  
d
[
r
+
1
]
−
x
d[l] + x, \; d[r+1] - x
d[l]+x,d[r+1]−x.
          
\;\;\;\;\;
单点查询可以转化为求前缀和:
a
[
i
]
=
(
d
[
i
]
−
d
[
i
−
1
]
)
+
(
d
[
i
−
1
]
−
d
[
i
−
2
]
)
+
⋅
⋅
⋅
+
(
d
[
2
]
−
d
[
1
]
)
a[i] = (d[i] - d[i-1]) + (d[i - 1] - d[i-2]) + \cdot \cdot \cdot + (d[2] - d[1])
a[i]=(d[i]−d[i−1])+(d[i−1]−d[i−2])+⋅⋅⋅+(d[2]−d[1]).
【接口】
          
\;\;\;\;\;
void update(int k, int x);
          
\;\;\;\;\;
复杂度:
O
(
l
o
g
n
)
O(logn)
O(logn)
          
\;\;\;\;\;
功能:单点修改
d
[
k
]
+
x
d[k] + x
d[k]+x.
          
\;\;\;\;\;
调用:update(l, x), update(r+1, -x).
          
\;\;\;\;\;
ll Query_sum(int i);
          
\;\;\;\;\;
复杂度:
O
(
l
o
g
n
)
O(logn)
O(logn)
          
\;\;\;\;\;
功能:返回前缀和
s
u
m
[
i
]
=
∑
k
=
1
i
d
[
k
]
sum[i] = \sum_{k=1}^{i} d[k]
sum[i]=∑k=1id[k].
          
\;\;\;\;\;
调用:a[i] = Query_sum(i).
【代码】
void update(int k, int x) {
for(int i = k; i <= n; i += lowbit(i)) {
c[i] += x;
}
return;
}
ll Query_sum(int k) {
ll sum = 0;
for(int i = k; i >= 1; i -= lowbit(i)) {
sum += c[i];
}
return sum;
}
1.3 区间修改 + 区间和
【任务】将区间
[
l
1
,
r
1
]
\left [ l_{1},r_{1} \right ]
[l1,r1] 中每个数
+
x
+x
+x , 查询区间和
[
l
2
,
r
2
]
\left [ l_{2},r_{2} \right ]
[l2,r2] 。
【说明】原始数组
a
[
i
]
a[i]
a[i], 树状数组
s
u
m
1
[
i
]
sum1[i]
sum1[i] 存储
d
[
i
]
d[i]
d[i],
d
[
i
]
=
a
[
i
]
−
a
[
i
−
1
]
d[i] = a[i] - a[i-1]
d[i]=a[i]−a[i−1].
          
\;\;\;\;\;
区间修改:可以转化为单点修改:对区间
[
l
,
r
]
[l,r]
[l,r] 中每个数
+
x
+x
+x 之后,对于
d
[
i
]
d[i]
d[i] 数组,区间
[
l
+
1
,
r
]
[l+1,r]
[l+1,r] 保持不变,只有两个地方需要改
d
[
l
]
+
x
,
  
d
[
r
+
1
]
−
x
d[l] + x, \; d[r+1] - x
d[l]+x,d[r+1]−x.
          
\;\;\;\;\;
区间求和: 前缀和
∑
i
=
1
r
a
[
i
]
=
a
1
+
a
2
+
.
.
.
+
a
r
\sum_{i=1}^{r} a[i] = a_{1} + a_{2} + ... + a_{r}
∑i=1ra[i]=a1+a2+...+ar
           \;\;\;\;\; = d 1 + ( d 1 + d 2 ) + ( d 1 + d 2 + d 3 ) + ⋅ ⋅ ⋅ + ( d 1 + d 2 + . . . + d r ) = d_{1} + (d_{1} + d_{2}) + (d_{1} + d_{2} + d_{3}) + \cdot \cdot \cdot + (d_{1} + d_{2} + ... + d_{r}) =d1+(d1+d2)+(d1+d2+d3)+⋅⋅⋅+(d1+d2+...+dr)
           \;\;\;\;\; = r ⋅ d 1 + ( r − 1 ) ⋅ d 2 + ( r − 2 ) ⋅ d 3 + ⋅ ⋅ ⋅ + 2 ⋅ d r − 1 + d r = r\cdot d_{1} + (r-1)\cdot d_{2} + (r-2) \cdot d_{3} + \cdot \cdot \cdot + 2\cdot d_{r-1} + d_{r} =r⋅d1+(r−1)⋅d2+(r−2)⋅d3+⋅⋅⋅+2⋅dr−1+dr
           \;\;\;\;\; = ∑ k = 1 r ( r + 1 − k ) ⋅ d k = \sum_{k=1}^{r} (r + 1 - k) \cdot d_{k} =∑k=1r(r+1−k)⋅dk
           \;\;\;\;\; = ∑ k = 1 r ( r + 1 ) ⋅ d k − ∑ k = 1 r k ⋅ d k = \sum_{k=1}^{r} (r + 1) \cdot d_{k} - \sum_{k=1}^{r} k \cdot d_{k} =∑k=1r(r+1)⋅dk−∑k=1rk⋅dk
           \;\;\;\;\; 需要第二个树状数组 s u m 2 [ i ] sum2[i] sum2[i] 存储 i ⋅ d [ i ] i \cdot d[i] i⋅d[i].
【接口】
          
\;\;\;\;\;
void update(int i, int x);
          
\;\;\;\;\;
复杂度:
O
(
l
o
g
n
)
O(logn)
O(logn)
          
\;\;\;\;\;
功能:单点修改
d
[
i
]
+
x
,
d[i] + x,
d[i]+x,.
          
\;\;\;\;\;
调用:update(l, x), update(r+1, -x).
          
\;\;\;\;\;
ll Query_sum(int i);
          
\;\;\;\;\;
复杂度:
O
(
l
o
g
n
)
O(logn)
O(logn)
          
\;\;\;\;\;
功能:返回前缀和
∑
k
=
1
i
a
[
k
]
\sum_{k=1}^{i} a[k]
∑k=1ia[k].
          
\;\;\;\;\;
调用:Query_sum( r ) - Query_sum( l - 1 ).
【代码】
void update(int k, ll x) {
for(int i = k; i <= n; i += lowbit(i)) {
sum1[i] += x;
sum2[i] += x * k;
}
return;
}
ll Query_sum(int k) {
ll sum = 0;
for(int i = k; i >= 1; i -= lowbit(i)) {
sum += sum1[i] * (k + 1) - sum2[i];
}
return sum;
}