数状数组模板

 树状数组,又称二叉索引树

一般解决动态连续和查询问题。

也就是说,

给定一个含有n个元素的数组A,要求你设计一个数据结构,支持2个操作

1.Add(x, d),也就是让第x个元素Ax增加d

2.Query(L, R)也就是计算AL + AL+1 + ...+AR

 

我们知道,在数据量很大的情况下,朴素的算法时间复杂度其实是非常高的。

所以现在要面临的问题就是如何让上述的两个操作花费的时间更少。

 

这里需要引进一个名为二叉索引树的概念。

二叉索引树:

 

 

在这之前,我们看看一个名为lowbit的概念。

啥是lowbit呢?

首先谈谈补码的概念。

假设 x = 11 它的二进制则为 x :1011

                  它的反码为:    x : 0100

       补码为反码末尾加一: x : 0101

OK,现在说说lowbit,它就是x&-x 

啥意思?

就是取x二进制位最右边的1所对应的值,比如11就是1(1011),38288(1001010110010000)就是16(10000)

也就是说 lowbit(x) =  2^k其中,k为x&-x中后缀0的长度

 

给出代码:

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

 

 

下面看看这个图:

 

可以发现,对于结点i,如果它是其子树的左子节点,则其父节点的编号为:i+lowbit(i);

反之如果其为右子树结点是,其父结点编号为:i-lowbit(i);

所以,我们开一个辅助数组C

Ci = A[i-lowbit(i)+1] + A[i-lowbit(i)+2] + A[i-lowbit(i)+3] +...+ A[i] 

可以发现

在BIT(二叉索引树)中,每个灰色的结点i都是以自身为结尾的水平长条(对于lowbit = 1的点输就是它自己) 

 

说了这么多,直接看图

 

下面是演示:

首先是求sum(11)的图:

可以知道(设A数组其从1开始)

C[11] = A[11]

C[11 - lowbit(11)] = C[10] = A[9] + A[10]

C[10 - lowbiot[10]] = C[8] = A[1] + A[2] + ...  + A[8]

C[8 - lowbit(8)] = C[0] = 0;

演示完毕。

 

给出代码:

int sum(int x)
{
    int ans = 0;
    while(x > 0)
    {
        ans += c[i]; x -= lowbit(x);
    }
    return ans;
}

 

 

这是单点更新操作的图示(假设更新点3):

下面解释一下:

 单点更新A[3],也就是更新C[3] = A[3]+x

C[3 + lowbit(3)] = C[4]+x

C[4 + lowvbit(4)] = C[8]+x

C[8 + lowbit(8)] = C[16]  = 0(由于16大于15)

演示结束,下面看代码

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

 

  

posted @ 2019-08-11 20:18  mpeter  阅读(138)  评论(0)    收藏  举报