zoukankan      html  css  js  c++  java
  • 树状数组初步

    树状数组(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,记得加备注。

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

  • 相关阅读:
    ZOJ 2158 Truck History
    Knight Moves (zoj 1091 poj2243)BFS
    poj 1270 Following Orders
    poj 2935 Basic Wall Maze (BFS)
    Holedox Moving (zoj 1361 poj 1324)bfs
    ZOJ 1083 Frame Stacking
    zoj 2193 Window Pains
    hdu1412{A} + {B}
    hdu2031进制转换
    openjudge最长单词
  • 原文地址:https://www.cnblogs.com/victorslave/p/4803277.html
Copyright © 2011-2022 走看看