树状·一目了然·数组

树状数组(是数组而不是树)

树状数组

用途:单点修改与区间求和,区间修改与单点取值(线段树都可以实现,不过显得大材小用)

树状数组利用二进制的思想,以上图为例,红色代表树状数组bit[],黑色代表原生数组arr[],则可直观看出

bit[1]=arr[1];

bit[2]=arr[1]+arr[2];

bit[6]=arr[5]+arr[6];

bit[12]=arr[9]+arr[10]+arr[11]+arr[12];

bit[14]=arr[13]+arr[14]

简单找规律(查资料)后发现,C[i] = A[i - 2k+1] + A[i - 2k+2] + … + A[i]; //k为i的二进制中从最低位到高位连续零的长度,即i的二进制表示中最后一位1及后面所有的0组成的数。

那么2k该怎么求呢?

参考本文第八个小知识点,可知2k即为i中最小的2的幂,而该知识点中判断的方法正好可以用来获得i的二进制表示中最后一位1及后面所有的0组成的数,即(i&(-i)).

//需要先构造lowbit函数或宏
//宏
#define lowbit(x) (x&(-x))
//函数
int lowbit(int x){
    return (x&(-x));
}
单点修改与区间求和

构造树状数组:

arr[i] 包含于 bit[i + 2k]、bit[(i + 2k) + 2k]…

​ 所需空间为线性,bit[max_num]即可(构造全局变量)

​ 随数据的输入直接构造即可(相当于原数据均为0,然后每次单点增加arr[i]大小的数据,因此,单点修改有着与之一样的操作):

void update(int index,int delta,int num){
    while(index<=num){
        bit[index]+=delta;
        index+=lowbit(index);
    }
}
...
for(int i=1;i<=num;i++){
    cin>>arr[i];
    update(i,arr[i],num);
}

区间求和:

int query(int index,int num){
    int ans=0;
    while(index>0){
        ans+=bit[index];
        index-=lowbit(index);
    }
    return ans;
}

上述代码求解的是 $ \sum_{i=1}^{index} $(arr[i]),要想求解区间x~y范围内的数据和,只需query(y)-query(x-1).

区间修改与单点取值

与单点修改不同,在区间修改的过程中,若是按上述过程对区间中的数据分别修改,则完全体现不出树状数组的优势,因此这里引入差分的概念。

差分

对于序列a1,a2,a3…an,对它进行差分就变成了a1 ,a2-a1 ,a3-a2…,an-an-1,差分后的序列的前i项和
sum=a1+(a2-a1)+(a3-a2)+…+(ai-1-ai-2)+(ai-ai-1)=ai

即从1到 i - 1 项都被消掉了,最后只剩下了ai,得出结论:差分后序列的前 i 项和等于差分前第 i 个元素的值,利用此性质再结合上面的query()函数,则可以快速进行单点取值。

那么差分在区间修改过程中有什么奇妙的应用呢?

由上述结论可以推出当原数组的连续区间[L,R]被统一修改时,其差分数组只需要修改index=L和index=R+1的两个数据,原因如下:

原因如下

  • 区间[L,R]内每个元素都加上了相同的值,所以相对差距并没有改变,所以[L+1,R]的差分值并没有改变,但是这个区间的两头却会发生改变;

  • 第L个元素加上了x,但第L-1个元素没有加x,所以第L个元素的差分结果就会从原来的(aL-aL-1)变成了现在的(aL-aL-1+x),增加了x;

  • 同理尾部第R个元素加上了x,但是第R+1个元素没有加x,所以第R+1个元素的查分结果就从原来的(aR+1-aR)变成了现在的(aR-aR-1-x),减少了x.

    void update(int index,int delta,int num){
        while(index<=num){
            bit[index]+=delta;
            index+=lowbit(index);
        }
    }
    
    int last=0,cur;
    for(int i = 1; i <= num; i++) {
    	cin>>cur;
        update(i, cur - last, n);
    	last = cur;
    }
    

洛谷树状数组板子练习:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值