树状数组
介绍
我们此前已经了解过前缀和与差分,在数组中,求前缀和的时间复杂度为 O ( n ) O(n) O(n),查询元素的时间复杂度为 O ( 1 ) O(1) O(1)。在前缀和数组中,求前缀和的时间复杂度为 O ( 1 ) O(1) O(1),查询元素的时间复杂度为 O ( n ) O(n) O(n),这两种数组的时间复杂度都是根据其最高的时间复杂度来确定的,即 O ( n ) O(n) O(n),那么有没有可能采取一个折衷的办法让修改和查询操作都不至于那么慢呢?树状数组出现了。
树状数组是一种高级数据结构,它主要支持两种操作:
- 快速修改数组中的一个数
add(int x,int c)
: O ( l o g n ) O(logn) O(logn) - 快速求出[
l,r]
的区间和ask(int x)
: O ( l o g n ) O(logn) O(logn)
树状数组支持前缀和
和元素修改
操作,且时间复杂度都为
O
(
l
o
g
n
)
O(logn)
O(logn)
先修知识
原理
以
8
8
8个数据
a
[
1
]
∼
a
[
8
]
a[1] \sim a[8]
a[1]∼a[8](下标从1
开始)为例,设置一个数组
t
[
8
]
t[8]
t[8],其中
t
[
i
]
t[i]
t[i]表示以i
结尾的长度为lowbit(i)
的区间和,图示如下:
将它看成一颗树,
t
[
8
]
t[8]
t[8]为根节点,它的儿子有
t
[
4
]
,
t
[
6
]
,
t
[
7
]
,
a
[
8
]
t[4],t[6],t[7],a[8]
t[4],t[6],t[7],a[8],
t
[
4
]
t[4]
t[4]的儿子有
t
[
2
]
,
t
[
3
]
,
a
[
4
]
t[2],t[3],a[4]
t[2],t[3],a[4],因此要动态维护一个前缀和,比如给
a
[
3
]
+
5
a[3] + 5
a[3]+5,只需要将
t
t
t中所有与
a
[
3
]
a[3]
a[3]有关的数加上
5
5
5即可。那么就出现了一个问题,如何找到
t
[
i
]
t[i]
t[i]的所有父节点呢?
寻找 t [ i ] t[i] t[i]的所有父节点
我们用二进制来找联系:
2
10
=
1
0
2
2_{10} = 10_2
210=102
3
10
=
1
1
2
3_{10} = 11_2
310=112
4
10
=
10
0
2
4_{10} = 100_2
410=1002
4是2、3的父亲,容易发现,
10
0
2
=
1
0
2
+
l
o
w
b
i
t
(
2
)
100_2 = 10_2 + lowbit(2)
1002=102+lowbit(2),
10
0
2
=
1
1
2
+
l
o
w
b
i
t
(
3
)
100_2 = 11_2 + lowbit(3)
1002=112+lowbit(3),即:
第i
个点的父亲是i + lowbit(i)
因此给第x
个数+c
只要给所有a[x]
的父节点都加上c
即可。
求前缀和
以
i
=
3
i = 3
i=3为例,要求
i
=
3
i = 3
i=3的前缀和需要求
t
[
3
]
+
t
[
2
]
t[3] + t[2]
t[3]+t[2]
3
10
=
1
1
2
3_{10} = 11_2
310=112
2
10
=
1
0
2
2_{10} = 10_2
210=102
以
i
=
=
4
i == 4
i==4为例,要求
i
=
4
i = 4
i=4的前缀和需要求
t
[
4
]
t[4]
t[4]
4
10
=
10
0
2
4_{10} = 100_2
410=1002
容易看出
1
1
2
=
1
1
2
11_2 = 11_2
112=112,
1
0
2
=
1
1
2
−
l
o
w
b
i
t
(
3
)
10_2 = 11_2 - lowbit(3)
102=112−lowbit(3),
0
2
=
1
0
2
−
l
o
w
b
i
t
(
2
)
0_2 = 10_2 - lowbit(2)
02=102−lowbit(2)
10
0
2
=
10
0
2
100 _2 = 100_2
1002=1002,
0
2
=
10
0
2
−
l
o
w
b
i
t
(
4
)
0_2 = 100_2 - lowbit(4)
02=1002−lowbit(4)
修改操作代码
void add(int x,int c){ // 维护a[x] + c
for(int i = x; i <= n; i += lowbit(i)) t[i] += c;
}
前缀和代码
int ask(int x){ // 求第x位的前缀和
int res = 0;
for(int i = x; i; i -= lowbit(i)) res += t[i];
return res;
}