蒟蒻树状数组初始记


7.24 树状数组浅浅预习

由于下一周训练要练习线段树相关内容,为了提高上课的效率同时发现同校的大佬们在自己写博客和题解的同时也提高了自己对知识的深入理解,获益匪浅,为了向大佬们看齐,因此提前浅浅的记录一下关于树状数组的预习内容。

                                                                  树状数组
                                                                                          ———————短小精悍的手术刀

可以解决的基本问题
1.单点更新和单点查询操作
2.单点更新和区间查询操作
3.区间查询和单点更新操作
4.区间查询和区间更新操作

首先简单的说一下个人浅浅的对树状数组的基本思想和原理的理解:
firstly,为什么会出现树状数组这个玩意?回忆一下前缀和,首先一个普通的数组在维护一段区间的时候,可以做到O(1)的单点更新和O(N)的区间查询,而前缀和则可以做到O(N)的区间查询和O(1)的单点更新,但是当N范围很大的时候线性的复杂度可能无法在规定的时间内解决问题,为了更好的解决区间的更新和查询的问题,人们搞出了一个树状数组可以在logn的复杂度内解决单点更新和区间查询的问题。

下面谈一下关于树状数组的设计思想和个人一点对它原理的简单理解:
首先树状数组这个玩意的英文名叫做Binary Indexed Tree,二叉索引树,它的基本设计思想是和快速幂一样是基于二进制的一种优化方式:
首先看这样一个问题看[1,10]这个区间,我们用二进制的思想把这个大区间划分成一个个更小的区间,10的二进制表示是1010,我们把它化成(8,10],(0,8]我们是咋划分的呢?是从尾部(10)开始一次弄 去掉一个10最后一位1,则10变成1000,也就是8,同理再拿8,把8去掉最后一个1,8变成了0,这样就把原来的区间划分成了两个小区间。不难看出对于任意一端给定区间[1,x]我们都可以将它基于这种规则进行划分,可能到这里我们还没有看出刚才弄得这玩意到底和树状数组有什么关系?不要着急,我们接着来看,观察刚才划分的区间长度之间有什么共性没有,可以看出每一个划分成的小区间(L,R]都是以一段以 R的二进制表示中,末尾最后一个1对应的十进制为长度的区间。那么怎么用语言定量表达出任意一个数字末尾最后一个1所在的位置?
这里联系上学期学的数字逻辑的基本知识,求x补码时候只需要从最后一个1开始后面的保持不变前面的都变成0,就是x的补码,然后再联系一下下学期学的系统基础的代码表达的知识,很容易能想出只需要将x&-x便可以优雅的得出我们需要的x的末尾最后一个1对应位置(很明显这里是得到的对应位置的十进制数字,也就是我们要弄的长度!)。
然后刚才这个弄长度的函数,我们一般叫它lowbit(x);
最后我们这样想,如果把刚才的区间以右端点R作为索引弄成一个数组C[R],它的含义其实就是[R-lowbit(R)+1,R]的这段区间,储存的值为原数组在这段区间上的和,上面说了这么多抽象的话,下面给出一个具体的图像








这样我们就浅浅的弄明白了树状数组的构造原理,
下面讨论树状数组是如何实现它的更新,查询操作的
1.区间查询
  给你一个x让你得出[1,x]这段区间的和,这里如果能理解上面的区间划分的思想的话,很显然就是不断从x进行末尾最后的1,来对应划分出的小区间,从小区间拿出它的值,不断的迭代,直至x为0结束这个过程,对应代码来看的话就是
for(int i=x;I;i-=lowbit(i))res+=tr[i];//res存储最终返回结果,tr[]数组就是对应划分出来的一个一个的小区间,也就是树状数组。

2.区间更新

从图像可以看出当你更新某一个点时,它会对他的前驱产生一连串的影想,我们理解区间更新操作就要弄明白子节点到底直接影响多少父节点,从图上来看很容易能看出来他是直接影响唯一一个父节点,下面简单证明一下:
首先看任意一个父节点它的儿子节点的情况,比如给定一个x的二进制表示(即给定区间为c[x]=[x-lowbit(x)+1,x])首先它一定包含a[x]本身,然后只需要弄出[x-lowbit(x)+1,x-1]包含哪些节点就可以了
***1011000,显然x-1为***1010111根据我们区间划分的原理是可以把它的子区间拿出来也就是(***1010110,x-1],(***1010100,***1010110],…….很容易看出每一个儿子区间的右端点都是原来x的二进制表示中以此去掉末尾最后1组成的集合。
有了这个理论基础,我们很容易的就可以看出给定一个子节点
例如0010100,它能直接影响到的节点是0011000也就是唯一影响性,怎么得到它直接影响的这一个节点呢?
很显然只要x加上lowbit(x)就可以了到此区间更新的操作也能看出来是一个迭代的过程
对应代码也就是for(int i=x;i<=n;i+=lowbit(i)])tr[i]+=c;

到此关于树状数组的基本原理就浅浅的介绍完了。
在代码上看起来比较简洁,但如果好好想一下它的原理的话,会发现它是如此的美妙,上述的讨论过程其实是在已知最后树状数组的样子下对树状数组的产生过程进行的一点点讨论。
不得不说当时设计树状数组的神奇,在这个过程中我也慢慢体会到算法的美丽,代码的简洁,下一篇的博客将从树状数组的拓展上介绍它的应用。

参考资料:acwing yxc 算法提高课 4.2树状数组

注:本蒟蒻只是将课上的知识加工了一下,包含自己的浅陋理解,蒟蒻是第一次学习这些算法,也是第一次写博客,主要目的还是监督自己学下去,也是加深对算法的理解,
排版混论,错误之处请大佬们多多包含,指教。



 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值