树状数组的作用:
一.快速的求给某区间内的某个数加上某一个数
二.在O(longn)下快速的求出前缀和。
关于二.前缀和可以用O(1)时间打表求出前缀和,为什么还要用树状数组呢?
因为前缀和只能查询,但是不能修改,而树状数组可以修改。
比如想把某段区间内的x修改为v:
那我们可以把x+(-X)+v=v。
假设V比X大的话,那C[y]([v,y]段前缀和) 的值也会因此变大,同理,C[z]的值也会受到影响,最坏的情况是后面所有的数都受到了影响,那么时间复杂度就为O(n)了,如果用树状数组,时间复杂度就为O(longn):
x值变了,整个前缀和的值也都变了。
树状数组画图
a[]代表原数组,C[]代表树状数组。
树状数组用来维护一个一维数组的信息,记住下标要从1开始
第0层存的都是原数组奇数位置上的值:
此时C[1]=a[1]
第1层存的是刚好可以被2^1整除的树:
C2=C[1]+a[2]
C6=C[5]+a[6]
C10=C9+a[10]
C14=C13+a[14]
第二层存的是刚好可以被2^2整除的树:
C4=C2+C3+a4
第2层存的是可以被2^3h整除的树:
C8=C4+C6+C7+a8
第3层存的是可以被2^4整除的数:
C16=C8+C12+C14+a16
根据最后一张图,我们总结这样一个规律:
C2是a1~a2的和
C4是a1~a4的和
C8是a1~a8的和
……
C16是a1~a16的和
Cn是前n段的和
树状数组求前缀和公式
怎么确定C[x]在第几层的?
其实是根据X的二进制有几个0判断它在第几层:
比如C[4],4的二进制是:0100,所以在第二层:
再比如C[8],二进制为:01000,所以在第三层:
C[X]求前缀和的公式为:
C[x]=(x-2^k,x](注意左开右闭)
2^k就是x的末尾有k个0的意思。
比图C[4]=a1+a2+a3+a4,我们套公式验证一下:(4-2^2,4]=(0,4](注意左开,不包含0,从a1开始)。
有一个函数叫lowbit(),这个函数的功能是你给它传x值,如果x二进制末尾有0,它就会返回2^k,否则返回-1.
因此树状数组求前缀和公式可以表示为: C[x]=(x-lowbit(x),x];
现在我们求出x段的前缀和了,那么我们要求整段的前缀和:
模板题
我们可以for循环遍历i,i为每个节点的下标,不断累加lowbit(i)就可以求出整段的前缀和了。
这道题让我们修改区间内某元素,求前缀和,可以用树状数组求: