简单的树状数组

简介

树状数组和线段树相比而言,树状数组的代码量明显偏少,仅对于单点修改或者是求前缀和之类的题目,树状数组真的是不二之选.


原理


如图所示,上面参差不齐的c数组管理着下面的a数组.
显然,c[2]管理的是a[1]、a[2],共2个;
c[4]管理的是a[1]、a[2]、a[3]、a[4],共4个;
c[6]管理的是a[5]、a[6],共2个;
c[8]管理的是a[1]到a[8],共8个;(全是2的多次方对不对,保持悬念就好了)
所以,如果要计算区间和的话,比如说a[100]到a[200],暴力完全可以,但如果是a[1000000]到a[10000000]的话,那就直接爆了.
而使用树状数组求区间和,则颇为方便,下面再作详细解答.
****

用法及操作

对于c数组中的每一个元素它所管理的元素个数,我们通过观察可以发现,在数值上它等于该元素的下标的二进制表示的从右往左数第一个“1”的权值。
举例1,c[8]的下标8二进制表示为1000,从右往左数第一个“1”在第4位,权值为pow(2,3)即8;
举例2,c[6]的下标6二进制表示为110,从右往左数第一个“1”在第2位,权值为pow(2,1)即2;
很清楚了,我们用lowbit来表示c数组中元素所管理的元素个数,通过前面的分析,这个lowbit要表示的是它二进制表示下最后一个“1”的权值,那么这个最后一个“1”的位置怎么得到呢?
举例x1=11001001000,我们将它取反,
得到x2=00110110111(前置0不清空),这个时候再+1,
得到x2=00110111000,这样子,x1&x2不就得到了1000么,不就表示的是lobit值么.总结上述得到lowbit(x)=x&(-x),-x表示的是x的二进制表示取反并+1,数值上等于x^(x—1),(其中^是异或);

int lowbit(int x) {return x&(-x);}
    

清楚lowbit之后,那么对于单点修改就轻松了;

void add(int x,int k)
{
    while(x<=n)
    {
        c[x]+=k;
        x+=lowbit(x);
    }
}

每次把上级都更新好了就行.
区间和也简单吧.

int getsum(int x)
{
    int ans=0;
    while(x>=1)
    {
        ans+=c[x];
        x-=lowbit(x);
    }
}

相比于区间修改以及区间查询,本人更喜欢使用线段树,而对于树状数组而言这两种操作使用差分思想也是可以做到的,就这样吧。
贴个区间修改区间查询的链接:

猜你喜欢

转载自www.cnblogs.com/jiranqq/p/11021350.html
今日推荐