树状数组 基础篇
*大家可以先了解一下—>线段树
引入
数列操作:
给定一个初始值都为0的序列,动态地修改一些位置上的数字,加上一个数,减去一个数,或者乘上一个数,然后动态地提出问题,问题的形式是求出一段数字的和.
1.可以看出,这棵树的构造用二分便可以实现.复杂度是logN.
2.每个结点用数组a来表示该结点所表示范围内的数据之和.
3.修改一个位置上数字的值,就是修改一个叶子结点的值,而当程序由叶子结点返回根节点的同时顺便修改掉路径上的结点的a数组的值.
4.对于询问的回答,可以直接查找i…j范围内的值,遇到分叉时就兵分两路,最后再合起来.也可以先找出0…i-1的值和0…j的值,两个值减一减就行了.后者的实际操作次数比前者小一些.
5.这样修改与维护的复杂度是logN.询问的复杂度也是logN,对于M次询问,复杂度是MlogN.
这个东西,对于线段树来说是一个时间和空间上的飞跃,在空间上,最多只要开到原数组的2倍,而线段树要四倍,在时间上也略快于线段树。(最重要是代码短,懒人的福音)
问题:区间是如何划分的
对于序列a,我们设一个数组C定义C[i] = a[i – 2^k + 1] + … + a[i],k为i在二进制下末尾0的个数
K的计算可以这样: 2^k=x & (-x)
Examply 以6为例
(6)10=(0110)2
(-6)10=(1010)2
6 & -6=2
结构看上去要比线段树复杂,实际~~~~很短(相对于线段树)
敲重点 它的原理
我们定义lowbit(n)=n&(-n)表示取出非负整数n在二进制表示下最低位的1以及它后边的0构成的数值
c[x]保存序列A的区间[x- lowbit(x)+1,x]中所有数的和。
该结构满足以下性质:
1.每个内部节点c[x]保存以它为根的子树中所有叶节点的和。
2.每个内部节点c[x]的子节点个数等于 lowbit(x)的大小。
3.除树根外,每个内部节点c[x]的父节点是c[x+ lowbit(x)]。
4.树的深度为O(logN)
运用
1.对某个元素进行加法操作
树状数组支持单点增加,意思是给序列中的某个数A[x]加上y,同时正确维护序列的前缀和。根据上面给出的树形结构和它的性质,只有节点c[x]及其所有祖先节点保存的“区间和”包含A[x],而任意一个节点的祖先至多只有logN个,我们逐一对它们的c值进行更新即可。下面的代码在O(logN)时间内执行单点增加操作。<