初学树状数组(BIT)

初学树状数组(BIT)

菜鸟来学习树状数组了(耶)
还是从一个经典的问题引入吧

问题

对于一个长度为n的数列,有如下两种操作:
1.将元素ak加上x
2.查询任意一段区间的和。
(当然,两种操作都要进行多次)
在上面的问题中,其实只要能求到a1+a2+…的值,就可以解决问题了

为什么要有树状数组

(以下纯粹为个人意会,如有误,轻喷)


对于任意一个数字,我们都可以将其表示为2的幂次的和的形式,而且这种表示唯一。原因:任意一个十进制数都可以表示为唯一 一个二进制数。
比如,我们想求出a1+a2+…+a16
又想求出a1+a2+…+a19
常规的,我们一个一个加过去,时间复杂度为O(n),如果要做很多次这种操作,时间话费就很大
但是,我们知道,16=2^4 ,19=2^4 +2^1 +2^0
那么,假设我们知道a1+a2+…+a16、a17+a18、a19的值,并且存下来,那么,求和是不是会快很多?(logn级别的)
那你也许会问:为什么不是保存a1,a2+a3,a4+a5+…+a19呢?
因为这样没法统一啊。比如说这样你就没法求a1+a2+…+a16。
于是,lowbit和树状数组就诞生了。

lowbit函数

#define lowbit(x) ((x)&-(x))
(x是正整数)

这个函数实际上是寻找x的二进制表示中最低位的1出现的位置。(补码)

x 1 2 3 4 5 6 7 8 9
x的二进制 1 10 11 100 101 110 111 1000 1001
lowbit(x) 1 2 1 4 1 2 1 8 1
tree[1]的值 a1
tree[2]的值 a1+a2
tree[3]的值 a3
tree[4]的值 a1+a2+a3+a4
tree[5]的值 a5
tree[6]的值 a5+a6
tree[7]的值 a7
tree[8]的值 a1+a2+…+a8

这样安排tree数组是和lowbit相对应的(看tree中项的个数)。不得不说安排很巧妙。当然这只是编程技巧罢了。

void add(int x,int d)
{
    
    
    while(x<=n)///n是数组中元素个数
    {
    
    
        tree[x]+=d;
        x+=lowbit(x);
    }
}
int sum(int x)
{
    
    
    int ans=0;
    while(x>0)
    {
    
    
        ans+=tree[x];
        x-=lowbit(x);
    }
    return ans;
}

add

add函数用在初始化tree数组和更新tree中元素。最后代码其实是不用一个叫a[]的数组来存元素的,直接用tree存就好了。
比如说读入第三个数字为4(或者把第三个数字加四).调用add(3,4)
那么,tree[3]肯定被更新为4了(tree[3]+=4)
通过x+=lowbit(x),可以把所有含有a3项的tree数组内容都更新了(实在是妙啊)

sum

sum的思路就是十进制数表示成2的幂次的思路。具体不再详细说明

大抵难点如上。(剩下的就是刷题了。。。)

猜你喜欢

转载自blog.csdn.net/booniebears/article/details/113063426