其实是数据结构。
智推连续几天给我推树状数组的模板,还放在第一位……
对着蓝书的图看了好几天才看懂,树状数组的另外一个名字是二叉索引树,指通过把一个数组抽象的变形成树状的以求得到树形数据结构的效果。有人说是线段树的阉割版,我不太清楚,树状数组应该是不支持区间修改加速的。
首先我们需要理解lowbit的概念,它指的是一个数转成二进制后位数最低的那个1表示的值。它具有一个特殊的性质但是为什么具有这个性质是无须证明也不用了解的。
然后我们画一个图,在一定范围内按lowbit的大小从上向下逐层分布,一层中按编号排序。不难发现这是棵二叉树。(见蓝书)我们让每个节点水平延伸lowbit(它的)-1长度的条,包括该节点在内的长条表示它覆盖的区间内的所有点的和或者其他什么性质。
并不一定非要纠结于这个图,然后我们发现lowbit的一个特殊性质就是一个区间的编号减去它的lowbit等于不包含这个区间的且在该区间前面的第一个(最靠右)的区间。一个区间的编号加上它的lowbit等于包含该区间的且最小(最靠左)的区间的编号。很玄学,对吧。
我们知道了lowbit的概念和它的性质,那么怎么求lowbit呢?我们知道,计算机中采用补码,也就是取一个数的负数等于这个数取反加一,以100为例:
01100100(100)
10011011(-99)//是-101还是-99?
10011100(-100)
00000100(对-100和100按位取与)
所以\(lowbit(x)=x&-x\)。更玄学了对吧。
对于查询区间和,在当前区间编号非零的情况下使结果加上当前区间,再去往上一个区间(减去lowbit)。可以得到以x为起点的前缀和。
对于单点修改,在当前区间编号小于等于n的时候使当前区间修改值。
至于预处理……对于每个元素,进行n次add修改操作就好了。板子贴在这里。
int lowbit(int x){ return x&-x; } int sum(int x){ int res=0; while(x){ res+=c[x]; x-=lowbit(x); } return res; } void add(int x,int d){ while(x<=n){ c[x]+=d; x+=lowbit(x); } }