树状数组主要就是解决这两个问题:
- 1、快速求前缀和 O(logn)
- 2、修改其中某一个数 O(logn)
如果用普通的静态数组来操作,操作1:则需要O(n)
,操作2:则需要O(1)
如果用前缀和数组来维护的话,操作1:则需要O(1)
,操作2:则需要O(n)
显然,两者不能兼顾
比如x = 13,二级制表示位 1101
可以这样拆分为:(L,R]这样的若干个区间
(x - 2^0 , x] —> (12,13] 长度为1
(x - 2^0 - 2^2 , x - 2^0] —> (8, 12] 长度为4
(x - 2^0 - 2^2 - 2^3, x - 2^0 - 2^2] —> (0, 8] 长度为8
其中区间 (L,R] 长度一定是等于R的二进制表示的最后一位1所对应的次幂
有个专门用来求解上述次幂的函数lowbit(x): return x & -x
原区间可以表示为:[R-lowbit(R)+1, R] ,这样的话这个区间的长度 = lowbit(R)
然后用C[R]表示原数组中 区间[R-lowbit(R)+1, R] 的总和
如何根据父节点去找跟他相关的子节点C[x]?
举例:x=8
第一个数: a8
第二个数: C7 —> 8-1
第三个数: C6 —> 7 - lowbit(7)
第四个数: C4 —> 6 - lowbit(6)
如何通过子节点去找父节点? —>对应进行修改操作
- 这个方式可以通过父节点找子节点进行理解,
- 假设p代表父节点,以二进制形式表示的话 1000
- 那么他的子节点i为:
…0111
…0110
…0100
- 通过子节点去找父节点,只需要在最开始位置为1,其余置为0即可,这个操作就是加上lowbit(i)
- 得到 p = i + lowbit(i)
每次往上找父节点,类似于二进制中会多一个0,总有个logx位,最多logx次,复杂度log(n)
比如 a[7] += c
for(int i=7; i<=n; i+=lowbit(i)) C[i] +=c;
//修改的为:
C[7],C[8],C[16]
查询:1~x
的和,比如x= 7
for(int x = i; i; i-=lowbit(i)) ans += C[i];
//累计的为:
C[7] ,C[6] ,C[4]