浅谈树状数组

树状数组

树状数组和线段树比较相似,又有所不同。
我甚至一度认为他们一样的,其实不是一样的

见图:可以很快发现这和线段树不一样
浅谈树状数组_第1张图片

这个树是不是很有规律:(num是树状数组,A是原来的数组)
num[1] = A[1]
num[2] = A[1]+A[2]
num[3] = A[3]
num[4] = A[1]+A[2]+A[3]+A[4]
……
在总结大量规律的时候,可以发现

num[i] = A[i - 2^k+1] + A[i - 2^k+2] + … + A[i];
k为i的二进制中从最低位到高位连续零的长度

有人问:如何求k,前辈给出的答案是:x&(-x)
这个时候我们会使用到位运算来辅助我们计算
这里利用的负数的存储特性,负数是以补码存储的,对于整数运算 x&(-x)有:
1.如果一开始是0,那么x&(-x) = 0
2.如果是奇数,负数取反加一。比如 :
	7 ---------> 0111
	-7---------> 1001
	奇数最后一位是1,奇数的负数是取反加1,最后一位肯定是1,结果是1
3.如果是偶数的话:
	6----------->0110
	-6---------->1010
	偶数的时候取反加一的时候会在最后出现的1一样,只有这样子才可以加起
	来取模为0,这时候得出来最后得零得长度,k出来了

到这里你应该很快明白了如何构造了
首先lowbit(上面得操作,叫做lowbit)
int lowbit(int x)
{
    return x&(-x);
}
void updata(int i,int k)
{    
    while(i <= n){
        num[i] += k;
        i += lowbit(i);
    }
}
int getsum(int i){        //求A[1 - i]的和
    int res = 0;
    while(i > 0){
        res += num[i];
        i -= lowbit(i);
    }
    return res;
}

出题目的人员很喜欢这种问法:

区间更新、单点查询:
	建议使用差分建树
区间更新、区间查询:
	差分建树
	A[0]+A[1]+……+A[n]
  =num[0]+(num[0]+num[1])+……+(num[0]+……+num[n])
  =n * (num[0] + …… + num[n]) - (num[1] * 0 + num[2]* 1+……+num[n]*(n-1))
  只需要维护两个树状数组就好了

你可能感兴趣的:(算法,补码,算法)