树状数组的三种模式

来源:互联网 发布:js for in return 编辑:程序博客网 时间:2024/05/21 00:48

我们在这之前已经了解过了lowbit的概念,还有对于树状数组的基本的认识了,然后,再学习树状数组的基础用法。

树状数组主要有三种类型,从简单到复杂。 依次是修改点,求区间和 ,修改区间求点,还有最复杂的修改区间,求区间。

最简单的第一种模型:

int lowbit(int x){return (x&(-x));}void  add(int x,int v)             //更新函数{for(;x<=N;x+=(lowbit(x))){a[x]+=v;}}int sum(int x)                  //求和函数{int summ=0;for(; x>0; x-=(lowbit(x))){summ+=a[x];}return summ;}int query(int i,int j)         // 查询函数{int temp;temp=sum(j)-sum(i-1);return temp;}

第二种模型,修改区间求点:

修改N次区间,让我求某一个点现在是多大了。

假设刚开始的时候所有的元素全部都是0,然后我们进行N次的区间修改,我们在这里引进一个数组B[],在这里数组B的概念很重要:B[x]=a代表的是1-x之间的每一个元素都增加了a。我们个实际的例子:

我们给区间1-5增加了5那么B[5]=5,给1-7,增加了10,那么B[7]=10,我们i=4这个点现在是多大? 好,我们再回头看看当时对B数组的定义:B[x]=a代表的是1-x之间的每一个元素都增加了a ,那么我们求 i=4这个点的时候,只要求B[4]+B[5]+B[6]+++++B[MAXN]; 所以这个例子,结果就是 B[4]+B[5]+B[6]+++++B[MAXN]=0+5+0+10+0+0++++0=15;

我们在用的时候要把B写成树状数组,也就是在B[5]=5,的时候要向下更新add(5,5),同理,B[7]=10,向下更新,add(7,10);  在求i=4的时候,向上求和,sum(4);

#include<iostream>using namespace std;const int MAXN=1000;int b[MAXN];int l,r;int lowbit(int x){return x&(-x);}void add(int x,int v){for(; x>0; x-=lowbit(x))b[x]+=v;}int query(int x){int sum=0;for(; x<=MAXN; x+=lowbit(x))sum+=b[x];return sum;}int main(){while(1){int v;cin>>l>>r>>v;   //区间   /区间加的值 v add(r,v);add(l-1,-v);cout<<"point:"<<endl;  //输入查询的点 int x;cin>>x;cout<<query(x)<<endl;}}

第三种模式区间修改,区间求和:

设A序列为初始的序列,B序列同第二种模型,B(i)表示对区间[1,i]每一个元素被加了多少。C(i)表示[1,i]整体每个数被加的数的总和。即对于ADD(x,c)(把区间[1,x]上的元素都加上c),B(x)+=c,C(x)+=c*x。
而ADD(x,c)是这样影响[1,i]的区间和的:若x<i,则会将A[1,i]上的区间和加上x*c,否则(即x>=i),会将A[1,i]上的区间和加上i*c。所以[1,i]的区间和=(B(i),...,B(N))*i加上C(1),...,C(i-1)。
这样很清晰了,我们举个例子,我们要求[1,10]区间的和。但是我们怎么求改的区间的呢?我们[1-20]这个区间加上5,[1,3]加上6,

很明显[1,10]包含在了[1,20]这个区间里,所以直接用模式二的求就好了。但是,在[1,10]里更改了一个小区间[1,3],我们不能用B[3]*10,而是用B[3]*3,但这样就意味着我们 需要记录之前进行了哪些更新的操作,并且我们还要判断哪些更新操作是对所求区间的子区间进行的更新操作,但这样的时间复杂度和空间复杂度就大大增加了.所以我们引进了另一个数组C[],来求像[1,3]这种在区间内的小区间的和。C[3]的含义是1-3,总共增加了多大,比如增加了x,那么C[3]=3*x;

所以我们想要求区间和,就分成了这两部分。所以[1,i]的区间和=(B(i),...,B(N))*i加上C(1),...,C(i-1)。

void add_B(int *a,int x,int v){for( ; x>0; x-=lowbit(x))a[x]+=v;}int sum_B(int *a,int x){int s=0;for(; x<MAXN; x+=lowbit(x)){s+=a[x];}return s;}void add_C(int *a, int x,int v){for( ; x<MAXN; x+=lowbit(x)){a[x]+=v;}}int sum_C(int *a, int x){int s=0;for( ; x>0; x-=lowbit(x)){s+=a[x];}return s;}

特别好的别人的一篇blog







原创粉丝点击