树状数组(binary index tree) 的主要作用是单点修改与区间查询(区间和等),而借用差分的思想,树状数组可以达到区间修改与区间查询的功能。而又由于BIT的常数比线段树小很多,所以很多用线段树解决的问题反而可以用BIT乱搞一通说不懂更快。
基本思想
//基本的BIT操作不再赘述
修改区间的时候,引用一个delta[]数组,查询前缀和的时候算上delta[]数组的贡献即可:
令sum[i]为数组a[]的前i项和,则:
\[\begin{split}
sum[i] = a[1] + a[2] + … + a[i - 1] + a[i] + delta[1] \times i \\+ delta[2] \times (i - 1) + … + delta[i - 1] \times 2 + delta[i] \times 1
\end{split}\]
其中,delta[$i$]为区间$[i,n]$的共同增量,而修改区间$[l, r]$时则可以将delta[$l$] += $x$, delta[$r + 1$] -= $x$.
单靠上面的式子肯定是不够的,进行变换一下:
\begin{equation} \begin{aligned} sum[i] &=\sum_{x=1}^ia[x]+\sum_{x=1}^idelta[x] \times (i + 1 - x) \\&= \sum_{x=1}^ia[x] + (i + 1) \times \sum_{x=1}^i delta[x] - \sum_{x=1}^idelta[x] \times x \end{aligned} \end{equation}
这样就很OK了。
然后,注意到式子中的delta[x], delta[x] * x我们只需要维护两个树状数组delta与deltai,分别记录delta[x]与delta[x]*x的前缀和。而sum[]数组则可以用O(n)来维护,因为其不需要单独修改。
如此一来,借用差分的思想,用数组记录区间修改带来的影响,然后用“增减量”来表示区间的和。
这样,查询区间[l, r]的和则可以用sum[r] - sum[l - 1]来求之。
实现
这样操作完以后,我们就实现了利用树状数组的区间修改与区间查询。
可惜大多数题目根本不会这么裸
模板题:CODEVS 1082 线段树练习
附上AC代码(比线段树快):