标签:
树状数组主要用于快速的更改某个点的值和查询某个区间的和,是一种比较小巧的数据结构.先看下图:
假设数组A[]是我们要操作的对象,则数组C[]则是数组A[]相对应的树状数组.观察上图,我们得到数组C[]前八个值:
C[1]=A[1]
C[2]=A[1]+A[2]
C[3]=A[3]
C[4]=A[1]+A[2]+A[3]+A[4]
C[5]=A[5]
C[6]=A[5]+A[6]
C[7]=A[1]
C[8]=A[1]+A[2]+A[3]+A[4]+A[5]+A[6]+A[7]+A[8]
.......
可以总结一个规律:当 i 为奇数时: C[i]=A[i];当 i 为偶数时: C[i]=A[i-2^k+1]+..A[i](k表示 i 最多有2的k次幂).
例如: 6的因子中有2的一次幂,等于2,所以C[6]=A[5]+A[6](由六向前数两个数的和),4的因子中有2的两次幂,等于4,所以C[4]=A[1]+A[2]+A[3]+A[4](由四向前数四个数的和).
对于任一个数组对应的树状数组有公式: C[n]=A[n-2^k+1]+....+A[n](其中k为n的二进制表示中从右往左数的0的个数)
对于求2^k有如下代码:
int lowbit(int x) { return x & (-x); }
求区间和操作
假设我们要求区间[1,7]的和,用sum(7)表示,观察上图可知: sum(7)=C[7]+C[6]+C[4].
具体代码如下:
// 求区间[1,i]的和 int sum(int i) { int s=0; while(i>0) { s += c[i]; i -= lowbit(i); } return s; }
更新某个点的值
当数组需要变更的时候,树状数组就发挥了它的优势,假设我们要给某个节点 i 的值加上 x.
算法包含两步:
① 当 i<=n 时,执行下一步;否则的话,算法结束;
② c[i] += x; i+=lowbit(i).
例如,在上面的示意图中,假设更改的元素是a[2],那么它影响到得c数组中的元素有c[2],c[4],c[8],我们只需一层一层往上修改就可以了,这个过程的最坏的复杂度也不过O(logN);
具体代码如下:
// 把节点 i 的值加上 x. void Update(int i, int x) { while(i<=n) { c[i] += x; i += lowbit(i); } }
int lowbit(int x) { return x&(-x); } void update(int x, int y, int val) //将 a[x][y] 的值增加val { for(int i=x; i<N; i+=lowbit(i)) { for(int j=y; j<N; j+=lowbit(j)) { sum[i][j] += val; } } } int getSum(int x, int y) //求以1,1为左上角端点,学校,x,y为右下角端点的矩阵和. { int s = 0; for(int i=x; i>0; i-=lowbit(i)) { for(int j=y; j>0; j-=lowbit(j)) { s += sum[i][j]; } } return s; }
标签:
原文地址:http://www.cnblogs.com/khan724/p/4375043.html