树状数组

C[i]代表每个节点下子节点权值之和

 

由图可知:
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];

 

化为二进制表示:

 

1=(001)      C[1]=A[1];

2=(010)      C[2]=A[1]+A[2];

3=(011)      C[3]=A[3];

4=(100)      C[4]=A[1]+A[2]+A[3]+A[4];

5=(101)      C[5]=A[5];

6=(110)      C[6]=A[5]+A[6];

7=(111)      C[7]=A[7];

8=(1000)    C[8]=A[1]+A[2]+A[3]+A[4]+A[5]+A[6]+A[7]+A[8];

 

得 :C[i]=A[i-k+1]+A[i-k+2]+......+A[i];
(k为i的二进制中位于最低位的1代表的数)
例如当i=8(1000), k=8;
       当i=7(111),   k=1;

 

引入lowbit(x) = k

int lowbit(x)

{
    return x&(-x);
}

证明lowbit():
首先明白一个概念,计算机中-i=(i的取反+1),也就是i的补码 
而lowbit,就是求(树状数组中)一个数二进制的1的最低位,例如01100110,lowbit=00000010;再例如01100000,lowbit=00100000。 
所以若一个数(先考虑四位)的二进制为abcd,那么其取反为(1-a)(1-b)(1-c)(1-d),那么其补码为(1-a)(1-b)(1-c)(2-d)。 
如果d为1,什么事都没有-_-|||但我们知道如果d为0,天理不容2Σ( ° △ °|||)︴ 
于是就要进位。如果c也为0,那么1-b又要加1,然后又有可能是1-a……直到碰见一个为补码为0的bit,我们假设这个bit的位置为x 
这个时候可以发现:是不是x之前的bit的补码都与其自身不同?,x之后的补码与其自身一样都是0? 
例如01101000,反码为10010111,补码为10011000,可以看到在原来数正数第五位前,补码的进位因第五位使其不会受到影响,于是0&1=0,; 
但在这个原来数“1”后,所有零的补码都会因加1而进位,导致在这个“1”后所有数都变成0,再加上0&0=0,所以他们运算结果也都是零; 
只有在这个数处,0+1=1,连锁反应停止,所以这个数就被确定啦O(∩_∩)O 
所以and以后只有x这个bit是一……

 

单点更新:每插入或更新一个节点,与之有关的节点的数随之改变
代码:

void add(int x, int y)         ///将第x个数加y
{
    for(int i=x; i<=n; i+=lowbit(i))
        tree[i] += y;
}

举例:

当更新A[3]时  需要向上更新C[3],C[4],C[8]

写为二进制  C[(010)],C[(100)],C[(1000)]

                            3(011)                          C[3]+=A[3]

lowbit(3)=001     3+lowbit(3)=4(100)     C[4]+=A[3]

lowbit(4)=100     4+lowbit(4)=8(1000)   C[8]+=A[3]

区间查询代码:

int getsum(int x)              ///前x个数的和
{
    int ans = 0;
    for(int i=x; i>0; i-=lowbit(i))
        ans += tree[i];
    return ans;
}
int ask(int l, int r)          ///l~r区间和
{
   return getsum(r) - getsum(l-1);
}

举例:

getsum(7):

                         7(111)                        ans+=C[7]

lowbit(7)=001  7-lowbit(7)=6(110)    ans+=C[6]

lowbit(6)=010  6-lowbit(6)=4(100)    ans+=C[4]

lowbit(4)=100  4-lowbit(4)=0(000)    


完整代码

#include <cstdio>
#include <iostream>
using namespace std;
int tree[1000001], n;
int lowbit(int t)
{
    return t&(-t);
}
void add(int x, int y)         ///将第x个数加y
{
    for(int i=x; i<=n; i+=lowbit(i))
        tree[i] += y;
}
int getsum(int x)              ///前x个数的和
{
    int ans = 0;
    for(int i=x; i>0; i-=lowbit(i))
        ans += tree[i];
    return ans;
}
int ask(int l, int r)          ///l~r区间和
{
    return getsum(r) - getsum(l-1);
}
int main()
{
    while(cin >> n)
    {
        for(int i=1; i<=n; i++)
        {
            int temp;
            cin >> temp;
            add(i, temp);
        }
        int l, r;
        while(scanf("%d%d", &l, &r) && l+r)
        {
            cout << ask(l, r) << endl;
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值