写在前面
话说挑个阴间时间发就没人看了罢(
这篇文章真的废了我很多心血,接下来几个月上学,可能没有很多时间写博客,所以难免会出现一些错误和讲得不够详细的地方,欢迎大家评论指出!
1 为什么要用树状数组
1.1 树状数组的用处
在做题的时候,经常会遇到“单点修改+区间查询”“区间修改+单点查询”“求逆序对个数”之类的问题。
如果只用一般的思路去写,很容易写出 的做法——
对于每个询问,遍历询问区间进行修改/查询。
但是当数据量加大的时候,就只能用树状数组优越的小常数以及 的时间复杂度进行优化。
1.2 树状数组?线段树?
另一种数据结构“线段树”与树状数组的时间复杂度大体相似,但我不会线段树
线段树的常数比树状数组略大一些,而且它的代码量更大;
不过它能解决的问题比树状数组多一点,但是树状数组也可以解决大部分线段树问题才怪
2 树状数组原理
2.1 思想
树状数组本质其实就是在维护一个前缀和/差分。
在“单点修改+区间查询”和求逆序对的时候用的就是前缀和的思想,“区间修改+单点查询”也要用到差分思想,所以不会前缀和和差分的点这里;
2.2 树?数组?
为什么把“数组”和“树”两个相反的词放在一起呢?
其实树状数组从实现上看是一个数组,它的定义是:
1) int lowbit(int x) 返回 x 转为二进制后最后一个 1 的位置所代表的数值
e.g. =
所以返回
=
2) 定义 tree 数组为 a 数组的树状数组 tree[i] = a[i-lowbit(i)+1] + a[i-lowbit(i)+2] + ... + a[i]
即
为什么说这是个树呢?请看VCR:
图中t[i]就是前面的 tree[i] , tree[i] 对应绿