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,记得加备注。

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

  • 相关阅读:
    php : Warning: strftime(): It is not safe to rely on the system's timezone settings.
    php : DOM 操作 XML
    php : 基础(3)
    php : 基础(2)
    阿里云物联网平台体验(NetGadgeteer+C#篇)
    阿里云物联网平台体验(树莓派+Nodejs篇)
    阿里云物联网平台体验(树莓派+Python篇)
    【MVP时间】5节课助你破解物联网硬件接入难点
    破解物联网落地困境-阿里云硬件接入最佳实践
    从端到云——工业物联网项目全栈快速开发实践
  • 原文地址:https://www.cnblogs.com/victorslave/p/4803277.html
Copyright © 2011-2022 走看看