树状数组浅讲

Posted santiego

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树状数组浅讲相关的知识,希望对你有一定的参考价值。

树状数组浅讲

rt,个人肤浅理解,各位神犇请自动出门右转

不同于传统数组每个元素单独存放,求和时遍历相加,树状数组每个元素不单独维护,而是被维护在一个包含其他元素的前缀和里。宜先仔细揣摩后再行。

技术分享图片

上图便体现了树状数组储存数据的原理

相当于以下等式

说明:C[]是树状数组,A[]是实际元素

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(即(A[1]))便包含在C[1],C[2],C[4],C[8]中,而如今有一个神器lowbit(x)=x&(-x)可以依次推出所有包含元素i的(C[k])们,我们先看看1至8的lowbit值

lowbit(1)=1

lowbit(2)=2

lowbit(3)=1

lowbit(4)=4

lowbit(5)=1

lowbit(6)=2

lowbit(7)=1

lowbit(8)=8

这时你会发现一个规律:每次当前下标再加上当前下标的lowbit值便会得到下一个包含元素i的(C[k])的下标,比如第一个包含元素1的(C[k])(C[1]),那么下一个(第二个)包含元素1的(C[k])(C[1+lowbit(1)]),如此按照此规律循环便可访问到所有包含元素i的(C[k])

于是下面遍历所有包含元素i的(C[k])的程序成立

void spot(int x)
{
    for(int i=x;i<=n;i+=lowbit(i))
        //do something
}

于是你又可以在遍历的同时顺便修改一下值(又于是下面修改元素i的值的程序成立)

void add(int x, int v)
{
    for(int i=x;i<=n;i+=lowbit(i))
        tree[i]+=v;
}

同理,你又会发现其实查询前缀和的规律也是如此,读者可以动手模拟一下

于是查询前缀和的程序又成立

int getsum(int x)
{
    int ans=0;
    for(int i=x;i>0;i-=lowbit(i))
        ans+=tree[i];
    return ans;
}

完整的树状数组板子

int lowbit(int x){return x&(-x);}
void add(int x, int v)
{
    for(int i=x;i<=n;i+=lowbit(i))
        tree[i]+=v;
}
int getsum(int x)
{
    int ans=0;
    for(int i=x;i>0;i-=lowbit(i))
        ans+=tree[i];
    return ans;
}

以上。

以上是关于树状数组浅讲的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript : 浅讲ajax

浅析树状数组

裴蜀定理 浅讲

神经网络浅讲:从神经元到深度学习

树状数组板子

数据结构之树状数组从零认识树状数组