算法心经——树状数组

树状数组

引例:
1. 给出任意n个元素,请计算前K(k<=n)项的和。
2. 现随机改变第m(m <= n)项的值,请计算前K(k<=n)项的和。

这就是我们今天要讨论的树状数组的问题。要解决引例中的问题,最简单也是最暴力的做法就是,每修改一次值就重新计算一次和,总共计算n次;根据经验可知,修改第m项数据,则m项之前的数据和是不变的,改变的只是包含于第m项的区间和。所以我们想能不能减少求和次数呢?
另开辟一个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[7];
C[8] = A[1] + A[2] + A[3] + A[4] + A[5] + A[6] + A[7] + A[8];
规则如下:

  C [ i ] = A [ i − 2 k + 1 ] + A [ i − 2 k + 2 ] + . . . + A [ i ] ; \ C[i] = A[i-2^{k}+1]+A[i-2^{k}+2]+...+A[i];  C[i]=A[i2k+1]+A[i2k+2]+...+A[i];

  其 中 , k 为 i 的 二 进 制 中 从 最 低 位 到 高 位 连 续 零 的 长 度 , 也 就 是 第 一 位 1 出 现 的 位 置 , 例 如 i = 8 ( 1000 ) 时 候 , k = 3. \ 其中,k为i的二进制中从最低位到高位连续零的长度,也就是第一位1出现的位置,例如i = 8(1000)时候,k = 3.  ki,1i=8(1000)k=3.

下面给出各项和的通项公式:

  S u m i = C [ i ] + C [ i − 2 k 1 ] + C [ i − 2 k 1 − 2 k 2 ] + . . . 其 中 k 1 = 0 , k 2 = 1... \ Sum_{i} = C[i]+C[i-2^{k_{1}}]+C[i-2^{k_{1}}-2^{k_{2}}]+... 其中k1=0,k2=1...  Sumi=C[i]+C[i2k1]+C[i2k12k2]+...k1=0,k2=1...

例如求前7项和,

  S u m 7 = C [ 7 ] + C [ 6 ] + C [ 4 ] , 根 据 图 表 所 示 , 是 正 确 的 . \ Sum_{7} = C[7]+C[6]+C[4],根据图表所示,是正确的.  Sum7=C[7]+C[6]+C[4].

  有 了 这 个 关 系 式 , 我 们 便 可 以 以 更 快 的 速 度 求 出 任 意 项 和 , 但 是 还 是 有 难 度 的 。 \ 有了这个关系式,我们便可以以更快的速度求出任意项和,但是还是有难度的。  便,
  首 先 是 2 k 问 题 , 我 们 先 给 出 结 论 : 2 k = i & ( − i ) \ 首先是 2^{k}问题,我们先给出结论 :2^{k}=i \&(-i)  2k2k=i&(i)

这是因为负数是以补码存储的,对于整数运算 x&(-x)
有当x为0时,即 0 & 0,结果为0;
当x为奇数时,最后一个比特位为1,取反加1没有进位,故x和-x除最后一位外前面的位正好相反,按位与结果为0。结果为1。
当x为偶数,且为2的m次方时,x的二进制表示中只有一位是1(从右往左的第m+1位),其右边有m位0,故x取反加1后,从右到左第有m个0,第m+1位及其左边全是1。这样,x& (-x) 得到的就是x。
当x为偶数,却不为2的m次方的形式时,可以写作   x = y × 2 k \ x=y\times 2^{k}  x=y×2k,其中,y的最低位为1。

对于该运算我们称之为lowbit,即取上述中的   2 k \ 2^{k}  2k.


示例代码

int n;//项个数
int a[2019],c[2019]; //对应原数组和树状数组
//求2^k,x为第几项
int lowbit(int x){
    return x&(-x);
}
//由于原数组的i位+k,而引起有关节点的改变
void updata(int i,int k){ 
    while(i <= n){
        c[i] += k;
        i += lowbit(i);
    }
}
//求第1项到第i项的和
int getsum(int i){
    int res = 0;
    while(i > 0){
        res += c[i];
        i -= lowbit(i);
    }
    return res;
}
上面的三个函数是核心,改造之后可以实现随机查询、区间求和等问题,需要铭记于心。
——The End——
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值