- 1、原创力文档(book118)网站文档一经付费(服务费),不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。。
- 2、本站所有内容均由合作方或网友上传,本站不对文档的完整性、权威性及其观点立场正确性做任何保证或承诺!文档内容仅供研究参考,付费前请自行鉴别。如您付费,意味着您自己接受本站规则且自行承担风险,本站不退款、不进行额外附加服务;查看《如何避免下载的几个坑》。如果您已付费下载过本站文档,您可以点击 这里二次下载。
- 3、如文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“版权申诉”(推荐),也可以打举报电话:400-050-0827(电话支持时间:9:00-18:30)。
- 4、该文档为VIP文档,如果想要下载,成为VIP会员后,下载免费。
- 5、成为VIP后,下载本文档将扣除1次下载权益。下载后,不支持退款、换文档。如有疑问请联系我们。
- 6、成为VIP后,您将拥有八大权益,权益包括:VIP文档下载权益、阅读免打扰、文档格式转换、高级专利检索、专属身份标志、高级客服、多端互通、版权登记。
- 7、VIP文档为合作方或网友上传,每下载1次, 网站将根据用户上传文档的质量评分、类型等,对文档贡献者给予高额补贴、流量扶持。如果你也想贡献VIP文档。上传文档
查看更多
优雅的数据结构---树状数组
优雅的数据结构
树状数组
2016
SJTU ACM Class
lwher
什么是树状数组?
• 其原理是什么?
• 它能做什么?
• 时空复杂度?
• 好不好写?
•
•太麻烦了!
•我们先来直接讲个例子,感受一下它的
神奇吧!
问题
• 给一个长度为n的数组,每次可以进行两种操作:
• (1)单点增加一个数值
• (2)查询给定区间的和
• n = 10^5 操作数=10^5
• 怎么做?
• 每次直接修改,然后查询的时候for一下?
• TLE!
• 利用前缀和每次快速查询,然后修改的时候重构?
• TLE!
• 啥?你说你会线段树?还会分块?
• ...Naive!
• 让我们来看看树状数组的代码是怎么写的吧。
• void Add(int x, int p){ //在x处增加p的值
• for(int i = x; i = n; i+= (i -i)) T[i] += p;
• }
• int Query(int x){//查询1~x的区间和
• int res = 0;
• for(int i = x; i; i -= (i -i)) res += T[i];
• return res;
• }
• 主函数
• 一开始我们认为数组全是0 , 然后如果数组有初始值,就
直接看成单点增加就OK 了
• 询问?假设问[l, r]和,那么答案就是Q(r) - Q(l - 1)
• 没了So 优雅!
• 很神奇吧?
• 那这么优雅的数据结构,它的原理是什么呢?
• 其实这个相对于它的代码来说,还是复杂了一点,我们来
慢慢看
• 首先解决一个问题:
• i -i 是啥?(后面也写作lowbit)
• -i 是在 i 的基础上把二进制的符号位变化,并且其他位取反
然后+1,是位运算,a b 的算法就是把a,b的二进制形
式右对齐,然后如果全是1,这位还是1,否则为0
• 举个例子 10100 的 lowbit 就是 100
• i 和 -i 实际上除了i的最后一个1那位,其他位置都不一样,
所以我们得到了i的二进制下最右边的那个1
• 接下来,我们来看看树状数组直观上看是长啥样的。
• 二进制十分的美妙!利用lowbit,我们发现从1~n刚好串成
了一个树形结构。
• 树状数组是如何实现快速求1~n位的和的呢?
• 其实通俗的来讲,树状数组的每一位C[i],除了存储num[i]
之外,还存了其与一些其他的数字加起来的和,我们可以
认为每个C[i]有一个自己负责的区域( 自己为根的子树),
那么我们在查询的时候,利用lowbit往下走,1~n这个区间
又被刚好分成了log个毫无交集的“区域” ,并且他们的并集
就是1~n,所以相加就好了(每次-=lowbit ,那么就是每次
消除二进制下最后一个1,最多log个1,所以是log次)
• 修改操作:
• 其实原理上差不多,每次+=lowbit,刚好实现了在树中一
层一层往上走的情况。因为每个C[i]统计的是以i为根的子
树的和,所以如果修改i的数字,那么i的祖先也都要一一修
改。
• (如果觉得上面的解释不好懂,那么我们换一种说法,树状
数组可以理解成每个C[i]有一个自己负责的“区域” ,然后关
系又是一级一级的,每个C[i]最多只会有一个“Boss” ,每个
C[i]负责自己的下属与下属的下属的和,那么修改的时候就
要“层层汇报” ,表示一下自己被改了,上级全部修改)
• 从理论上来解释一发
• 存储原理:
• 对于每个C[i]负责的区域,其实是这个i二进制表示下,去
掉去lowbit再+1 一直到 i 这个区间。
• 比如: 1101000负责的是1100001 ~ 1101000这个部分
• 所以我们修改的时候,每次加lowbit,相当于把最后的1一
直左移,遇到前面也是1的就也消去它再往前走。
• 查询也是一样的,每次查询 [x-lowbit(x)+1,x]然后去掉
• 比如1010 - 先查了1001 ~ 1010 ,然后x变成1000,
• 接着加上 0001 ~ 1000 这刚好是前缀和。
• 时间复杂
原创力文档


文档评论(0)