树状数组学习

Posted Bestcoderg

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了树状数组学习相关的知识,希望对你有一定的参考价值。

树状数组的功能和线段树一样。但是,这个东西是真的好写@。@

学习的博客:树状数组

  树状数组主要的话可以实现三个功能①单点修改,区间查询②区间修改,单点查询.3、区间修改,区间查询。树状数组和线段树思想有点像,就是通过某个点的值来代替区间值,实现区间的运算。

技术分享图片

  首先,一个很重要的操作是(x&-x)这个式子的结果是"从低位向高位数,第一个数字1"。我们将(x&-x)作为x管理的区间长度,如图所示。这个时候,我们发现x+(x&-x)的这个值所管理的区间也会到x,那么这样递归下去就可以将所有管理x的区间都改变值(改变操作)。求和时则不断向下递归,就能求出区间和。//接下来代码要不要加上原数组的值要视情况而定。

 1 void add(int x,int num)
 2 {
 3     for(;x<=MAX_N;x+=(x&-x))
 4         tree[x] += num;
 5 }
 6 int sum(int x)
 7 {
 8     int s = 0;
 9     for(;x>0;x-=(x&-x))
10         s += tree[x];
11     return s;
12 }

1、单点修改,区间查询:

  每次改变点的值,再sum减一下就好了。sum(a)-sum[b-1]。(是b-1,因为求得是闭区间的和)

关于后两种方法,另一个博客写的比较清晰:树状数组2

2、区间修改,单点查询:(注意这里只能单点查询)

  思想其实是化区间修改为点修改(这里的思想有点像是求前缀和)。要在(l,r)区间+k,则在l位置+k表示l及后面的位置都加了k,再在r+1的位置-k,表示从r+1开始的位置都减了k,这样其实就实现了区间的修改。把区间[L,R]的数都加上X的操作就变成了两个点修改:B[L]+=X  和  B[R+1]-=X; //这里是简写,还需要更新它们的父节点。(sum[x]);

3、区间修改,区间查询:(引用别人吧@。@)

  首先引入delta数组 delta[i]表示区间 [i, n] 的共同增量 于是修改区间 [l, r] 时修改 delta[l] 和 delta[r + 1] 即可(就是差分的思路)。查询的时候是查询区间 [l, r] 的和 即sum[r] - sum[l - 1] 所以现在的问题是求sum[i]。

1 sum[i] = a[1]+...+a[i] + delta[1]*i + delta[2]*(i - 1) + delta[3]*(i - 2)+...+delta[i]*1   // a[i]为原始数组
2        = presum( a[x] ) + presum( delta[x]  *  (i + 1 - x) )
3        = presum( a[x] ) + (i + 1) * presum( delta[x] ) - presum( delta[x] * x )

其中 sigma( a[x] ) 是可以预处理出来的 于是只需要维护 delta[x] 与 delta[x] * x 的前缀和(作为两个树状数组就可以了)

用两个树状数组,分别叫做d和s,d维护差分,s维护x*d[x]。

①进行区间修改操作时:

update(d,l,x);update(d,r+1,-x);

update(s,l,x*l);update(s,r+1,-x*(r+1));

②进行区间查询操作时:

sum(L,R)=sum(1,R)-sum(1,L-1)

sum(1,L-1)=L*query(d,L-1)-query(s,L-1)

sum(1,R)=(R+1)*query(d,R)-query(s,R)

 

 

以上是关于树状数组学习的主要内容,如果未能解决你的问题,请参考以下文章

(填坑)树状数组1

树状数组 浅显学习

树状数组学习

树状数组review学习

数据结构:树状数组 学习笔记

学习笔记1-回顾树状数组与莫队思路