树状数组总结【全】

【前言】

 

费劲千辛万苦,呕心沥血的大总结终于写好了。

 

主要包括了树状数组的三大应用以及详细的解释。

 

在此还要感谢http://www.cppblog.com/MatoNo1/archive/2011/03/19/142226.aspx的博文。

 

其实主要还是从中学习的,不过我想讲的更详细一点,更好理解一点。

 

期间还参考了好多博文,在此谢过~

 

阅读此文前请先阅读树状数组相关资料。

 

 

【应用一:改点求点】

 

这个是打酱油的应用。

 

完全不需要用到树状数组。

 

直接开个数组模拟就行了。

   

 

【应用二:改点求段】

 

也就是所谓求和。这也算是最基本的应用

 

可以参考:http://blog.csdn.net/Human_CK/archive/2011/06/13/6540532.aspx

               http://blog.csdn.net/Human_CK/archive/2011/06/12/6539722.aspx

 

三个函数在其中也是写的相当清楚。

 

求任意两点之间的和时也是用到了容斥原理。

 

这里要说明的一点是,“改点求段”的过程中,修改时是从左向右修改的(+lowbit(x)),求和时是从右向左求和的(-lowbit(x))。

 

原因很简单,修改时总是修改跟其相关的上节点,求和时分为两部分,一部分是当前管辖的区域,另一部分是非管辖的区域。

 

在下面第三个应用中会详细说到。

 

 

【应用三:改段求点】

 

参考其应用(二维):http://blog.csdn.net/Human_CK/archive/2011/06/14/6542909.aspx

 

后面的两个应用是重要且难以理解的。

 

如果数据规模比较小,可以直接用普通循环去修改,然后定义树状数组直接求和。

 

但是数据规模比较大时这个方法就太费劲了。

 

假设num数组用于存储原有数据。a数组用于树状数组的求和。

 

首先,需要一个辅助数组b。b存储的是一个改变量d,这个改变量表示num[1~i]的数都加上了d。

 

这样有什么好处呢?

 

如果要知道num[i]总共改变了多少,只需要知道num[i]之后的改变量的总和,即b[i~n]。也就是说,b[i]表示有对num[i]进行b[i]的改变(因为b[]的改变范围是1到n),b[i+1]表示有对num[i]进行b[i+1]的改变,……知道b[n]对num[i]进行b[n]的改变。

 

所以只要知道b[i~n]的值就可以算出对num[i]的总改变量。

 

那么这个b[i~n]的总和怎么求呢?

 

回到第二个应用,修改的时候是+lowbit,求和的时候是-lowbit。那么只要把它倒过来,修改的时候-lowbit,求和的时候-lowbit,就是我们所要求的结果。

 

可是为什么要这么做呢?

 

 

看这个图。我们平时计算用的都是这个图。这样的求法可以求出其左边的和。

 

反过来,节点1管辖1,节点2管辖(2,3),节点4管辖(4,5,6,7),其中节点6管辖(6,7),……如此这般。画个图就可以看明白的。

 

所以,倒过来求,就可以求出右边的和了。

 

到这里b[i~n]的和求出来了,表示为sum_B(i),这个值就是num[i]的改变量。每次要求某个节点的值时就是num[i]+sum_B(i)。

  

 

【应用四:改段求段】

 

这里还需要另一个数组c。c表示从1到i的总改变量。

 

每次在b[i]上增加d时,只需要在c[i]上增加d*i,表示增加1到i个数同时增加d的总和。

 

那么此时的总和怎么算呢?

 

假设要求sum(i)(表示求1到i的总和),那么b[i]之后的每个数值都表示对这个范围有改变,所以sum应该包括b[i~n]*i。

 

另外,i之前的所有数本来就有一个改变量为c,所以sum又得包括c[1~(i-1)](这里只计算到i-1,因为i被前面的b[]算过了)。

 

所以这里的sum_B也是反向维护(这里的b[]跟应用三中的b一样),而c则属于正常维护。

 

需要注意当增量为0时不可以进行操作,否则会越界。

 

求某段和时也是使用了容斥原理。

 

 

【代码】

 

 

 

【P.S】

 

上面这份代码也是写得挺烦的,主要是考虑了函数的精简。

 

今天研究了一整天,终于算是弄懂了。

 

好好休息一下~

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值