树状数组(Binary Indexed Tree,简称BIT),是一种修改和查询复杂度都为O(logN)的数据结构。但树状数组仅支持单点修改,在查询时,树状数组也要求被查询的区间具备可区间加减的性质。不过,树状数组因为代码实现容易、占用空间小,经常使用于代替线段树。html
在这里,咱们定义原序列为a,树状数组为c,则有:算法
其中,k为i的二进制表示中末尾0的个数,例如:i=3(101)时,k=0;i=8(1000)时,k=3。数组
定义函数lowbit(x)=2k(k为x的二进制表示中末尾0的个数),利用机器补码的性质,获得:数据结构
1 int lowbit(int x) 2 { 3 return x&(-x); 4 }
根据定义,咱们能够列出(这里咱们用a[i,j]表示a[i]~a[j]间的全部元素的信息):ide
c[1]=a[1]函数
c[2]=a[1,2]spa
c[3]=a[3]3d
c[4]=a[1,4]code
c[5]=a[5]htm
c[6]=a[5,6]
c[7]=a[7]
c[8]=a[1,8]
c[9]=a[9]
c[10]=a[9,10]
……
首先,由树状数组的定义,咱们能够获得一个性质:
•a[x]在树状数组中第一次出如今c[x],而且c[x]包含的区间右端点为x
此外,经过观察,咱们能够发现:
•a[x]仅对c[x]、c[x+lowbit(x)]、c[x+lowbit(x)+lowbit(x+lowbit(x))]……产生影响
相似于二进制拆分的思想,咱们还能够发现:
•c[1]~c[x]能够还原出a[1]~a[x]的全部信息
因此,树状数组的空间复杂度就为O(n)。
那么,咱们就能够写出维护树状数组的代码(这里咱们的树状数组查询的信息为区间的和):
1 void update(int x,int val) 2 { 3 for(;x<=n;x+=lowbit(x)) 4 c[x]+=val; 5 return; 6 }
接下来,若是咱们要求区间a[i,j]的和,咱们又要怎么作呢?
不妨这样想,a[i,j]的和其实等于a[1,j]的和减去a[1,i-1]的和,那么咱们只须要知道如何求一个的序列前缀和就能够了。
首先,根据定义,咱们知道:
•c[x]包含的区间长度为lowbit(x)
在这个性质下,与维护操做相似,咱们能够很轻松地写出求前缀和的代码:
1 int query(int x) 2 { 3 int ans=0; 4 for(;x;x-=lowbit(x)) 5 ans+=c[x]; 6 return ans; 7 }
以上,就是树状数组的基本内容了,下面咱们来看一道例题。
一行N个方格,开始每一个格子里都有一个整数。如今动态地提出一些问题和修改:提问的形式是求某一个特定的子区间[a,b]中全部元素的和;修改的规则是指定某一个格子x,加上或者减去一个特定的值A。如今要求你能对每一个提问做出正确的回答。1≤N<100000,,提问和修改的总数m<10000条。
输入文件第一行为一个整数N,接下来是n行n个整数,表示格子中原来的整数。接下一个正整数m,再接下来有m行,表示m个询问,第一个整数表示询问代号,询问代号1表示增长,后面的两个数x和A表示给位置X上的数值增长A,询问代号2表示区间求和,后面两个整数表示a和b,表示要求[a,b]之间的区间和。
共m行,每一个整数
6
4
5
6
2
1
3
4
1 3 5
2 1 4
1 1 9
2 2 6
22
22
1≤N≤100000, m≤10000 。
附上原题连接→_→|1080 线段树练习|CODEVS,算法爱好者社区
虽然是线段树练习,但在开头咱们便讲过,树状数组在必定状况下能够替代线段树。
如下内容是一个树状数组单点维护并求区间和的模板,能够经过CodeVs1080。其实核心代码都在上文出现过,这里只是作一个整理。
1 #include<cstdio> 2 const int MAXN=1e5+10; 3 int n,m; 4 int lowbit(int x){return x&(-x);} 5 int BIT[MAXN]; 6 void update(int x,int val) 7 { 8 for(;x<=n;x+=lowbit(x)) 9 BIT[x]+=val; 10 } 11 int query(int x) 12 { 13 int ans=0; 14 for(;x;x-=lowbit(x)) 15 ans+=BIT[x]; 16 return ans; 17 } 18 int main() 19 { 20 scanf("%d",&n); 21 for(int i=1;i<=n;++i) 22 { 23 int a; 24 scanf("%d",&a); 25 update(i,a); 26 } 27 scanf("%d",&m); 28 for(int i=1;i<=m;++i) 29 { 30 int flag,l,r; 31 scanf("%d%d%d",&flag,&l,&r); 32 if(flag==1)update(l,r); 33 else 34 { 35 int ans=query(r)-query(l-1); 36 printf("%d\n",ans); 37 } 38 } 39 return 0; 40 }
弱弱地说一句,本蒟蒻码字也不容易,转载请注明出处http://www.cnblogs.com/Maki-Nishikino/p/6217811.html