树状数组入门
讲树状数组之前呢,大家先来看看这个东西,lowbit
lowbit:其实也就是X&-X
这个东西会得到什么呢?
这个东西会得到一个数X的二进制表示形式中哪一位是1(具体原理不解释了,其实就是补码的知识),比如说X=20(十进制),20的二进制表示形式是10100,那么我们就会得到100(二进制),也就是4
那么这个东西能干啥呢。那,很明显,对于一个数我们不断的减去它的lowbit的值,我们就能够把这个数分解,比如说这个10100(二进制),我们先减去一个100(二进制),在减去一个10000(二进制),就是将他分解成了10000+100(二进制)。
那么这个拆分能用来搞什么呢?
很明显,如果我们能够构造一个数据结构,他能够O(1)提供a[……1000……0000]与a[……00000……001]之间所有的数的和的话那么我们就能很快速的求出a[1]+…+a[x] (利用前面说的分解)。
这个想法很好,怎么构造这个数据结构呢,我们假设我们这个结构中的每一个点对应着一个原数据中的一个点,那么我们经过观察容易得出,对于a[x],他对f[x]有一个直接的贡献,他还会对f[x+lowbit(x)]有一个直接的贡献,还会对f[x+lowbit(x)+lowbit(x+lowbit(x))]有一个贡献,他会这么一直递归下去(想必你已经知道是如何递归的,不知道就自己观察观察),直到边界。
那么说了那么一大堆,我们就有了我们最后要的数据结构,树状数组,他由+lowbit的操作构造,由-lowbit的操作求前缀和,求出来前缀和我们还能拿它出来询问区间。
具体这个东西就长这个样子(进阶指南的板子):
int c[x];//树状数组本体
void add(int x,int y){//x是位置,y是贡献
for(;x<=N;x+=x&-x)C[x]+=y//N是原数组的大小,是边界。
}
int ask(int x){
int ans=0;
for(;x;x-=x&-x)ans+=c[x];
return ans;
}

浙公网安备 33010602011771号