树状数组初步

树状数组(Binary Indexed Tree(BIT), Fenwick Tree)是一个查询和修改复杂度都为log(n)的数据结构。主要用于查询任意两位之间的所有元素之和,但是每次只能修改一个元素的值;经过简单修改可 以在log(n)的复杂度下进行范围修改,但是这时只能查询其中一个元素的值,且其常数之小是线段树无法做到的。

如图,其中a数组保存了初始读入的n个值,c数组即为我们构建的树状数组。

那么容易发现:

C1 = A1
C2 = A1 + A2
C3 = A3
C4 = A1 + A2 + A3 + A4
C5 = A5
C6 = A5 + A6
C7 = A7
C8 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8
 
由此,我们成功存下了2^n前缀和,可以轻松在o(1)时间内求解该前缀和。
如果前缀长不是2^n形式,我们又该怎么做呢?
首先介绍一个函数——lowbit,代码如下:
function lowbit(x:longint):longint;
begin
                exit(x and (-x));
end;

为什么要用这样一个函数呢?事实上经过测试我们发现,对于1-n内某数x,lowbit(x)表示其“管辖”着从它向后共2^lowbit(x)个数的和,而通过这种方法我们可以在不超过logn的时间内求出所有的lowbit加和,得到的就是该长度下的前缀和。

不难证明,对于1-n内的两个数i,j,满足i<=j,sum(i,j)=sum(1,j)-sum(1,i-1);

故对于任意区间,我们都可以用这种方法求解该区间内所有值的和,代码贴上

        function sum(l,r:longint):longint;
        var
                s1,s2:longint;
                ans1,ans2:longint;
        begin
                s1:=l-1;
                s2:=r;
                ans1:=0;
                ans2:=0;
                while s1>0 do
                begin
                        inc(ans1,bit[s1]);
                        dec(s1,lowbit(s1));
                end;
                while s2>0 do
                begin
                        inc(ans2,bit[s2]);
                        dec(s2,lowbit(s2));
                end;
                exit(ans2-ans1);
        end;

理解代码后建议自己打一遍,代码细节很多比较容易错

那么对于这样一棵树,它是怎么被构造出来的呢?

事实上同样利用了我们的lowbit函数,通过确定自己的“管辖范围”求出该点下的权值。
继续贴代码(这段比较容易懂,就不多说了)
        procedure in_in(pos,x:longint);
        begin
                while pos<=n do
                begin
                        inc(bit[pos],x);
                        inc(pos,lowbit(pos));
                end;
        end;

当然,对于这样一个神奇的数据结构我们必须了解它的局限性,即——无法进行区间修改(神犇别跟我说什么可以的话,一旦区间修改就只能单点查询一般用不到好的不送= =)

而单点修改也并非仅修改原来的a数组,而是同时修改你所用的“带有区间性质”的数组

单点修改分两种:

1)将一个点的值增加或减少某个值

方法:将其值修改后,用lowbit求出其父节点及之上的节点

直接贴代码,如果lowbit理解深入一定能看懂

        procedure change2(x,y:longint);
        begin
                while x<=n do
                begin
                        inc(bit[x],y);
                        inc(x,lowbit(x));
                end;
        end;

2)讲一个点的值改成另一个值

方法:很简单,求出修改后值和修改前值的差即可,再一次变成方法一修改233

代码贴一个

        procedure change1(x,y:longint);
        var
                ans1,ans2:longint;
                s1,s2:longint;
        begin
                s1:=y-a[x];
                s2:=x;
                while s2<=n do
                begin
                        inc(bit[s2],s1);
                        inc(s2,lowbit(s2));
                end;
        end;

就是这样,以上所有的代码都可以直接使用,加上变量和主程序就是一个完整的代码。

以上就是本人对于树状数组的一点小心得,如果有问题加我qq:1064864324,记得加备注。

喜欢就收藏一下,记得关注订阅哦亲(卖萌中~~)

posted @ 2015-09-12 17:24  ROLL-THE-FIRST  阅读(176)  评论(0编辑  收藏  举报