您的位置:首页 > 其它

树状数组初步

2015-09-12 17:24 274 查看
树状数组(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,记得加备注。

喜欢就收藏一下,记得关注订阅哦亲(卖萌中~~)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: