下面代码与字都是蒟蒻自己一个一个字打得,如有错误,请大佬指出(我实在太菜了)
这有神犇 boish 的 树状数组心得
写得比这一篇好多了前置知识:搞懂树状数组
树状数组最基本的操作:单点修改 (logN)+区间查询 (logN),上方已经有了,这里就不提了;
其实树状数组还有很多作用
废话不多讲,先看题 【模板】树状数组 2
其实这就是
区间修改+单点查询
这利用差分思想
原本:设差分数组 b[i]=a[i]−a[i−1],则 a[i]=b[1]+…+b[i](b[1]=a[1]−a[0],a[0]=0)
b[1]+...+b[i]
这个东西是不是很熟悉?
没错,这就前缀和
如何快速的求和,这就用树状数组存处差分数组即可
那么如何修改?
设要修改的区间是 [x,y]加上 k
我们 b 数组是差分数组,差分的修改只要在 x 处加 k,y+1 处减 k 就行了
同理在树状数组上进行同样操作,code:
时间复杂度:区间修改 (logN) 与单点求和 (logN)
(滑稽的分割线)
区间修改+区间查询
线段树 1
看到这个,一定会想到线段树,可蒟蒻不会,只好用树状数组(其实代码挺短的)
这里还是运用差分思想
区间修改还是和以前一样
那么如何查询???
我们来看式子
a[1]+a[2]+…+a[n]
=(c[1])+(c[1]+c[2])+…+(c[1]+c[2]+…+c[n])
利用乘法分配率:可以得
n∗c[1]+(n−1)∗c[2]+…+c[n]
然后就得到了神奇的式子~高能预警
n∗(c[1]+c[2]+…+c[n])–(0∗c[1]+1∗c[2]+…+(n−1)∗c[n])
由公式可以发现 我们要进行区间修改和区间查询只需要再维护一个数组 C2[i]=(i−1)∗C[i]即可
代码如下:
区间最值
先看题:
I Hate It
很多人都认为,树状数组不能求解最值,那是应为把树状数组看成前缀和
but,cssyz 金牌教练把树状数组叫做
二进制分块
为啥?
看张图(来自 boshi 大佬)
每一个下标,表示的是一块区间
前面求区间和,是把区间合在一起就行了
同理,区间最值也应该是全部块的最大值。
先来看 [1,n]区间最值
每一个下标,比如 11001000,就是下标 1100xxxx 这些的最大值
我们可以写出这样维护的代码
每一次更新一个数时候,把整个树状数组全部更新一遍
但显然,复杂度太高,O(nlogn)
我们再想一想:
对于每一个 c[i],在保证 c[1…i−1]都正确的前提下,要重新计算 c[i]值的时间复杂度是 O(logn)
so 我们可以换一种建区间维护方法
显然这个算法的时间复杂度是 O((logn)2)
现在维护好了,问题是如何查询?
直接照搬求区间合的方法显然是不行的。
因为区间合中,要查询 [x,y]的区间合,是求出 [1,x−1]的合与 [1,y]的合,然后相减就得出了 [x,y]区间的合。
而区间最值是没有这个性质的,所以只能够换一个思路。
因为 c[y]表示的是 [y,y−lowbit(y)+1]的最大值 (看图,就是 c[y]的区间)。
所以,可以这样求解:
若 y−lowbit(y)>x ,则 query(x,y)=max(c[y],query(x,y−lowbit(y)))
若 y−lowbit(y)≤x,则 query(x,y)=max(a[y],query(x,y−1))
此处可能有点难理解,多琢磨一下
蒟蒻就是会这些;
但有了这些其实可以做很多(例如,求逆序对….)
4 条评论
juruo-oier · 2018年8月25日 8:29 下午
海星!
其实我写得很烂
并不能讲清楚
XZYQvQ · 2018年8月25日 9:06 下午
对惹。。。XZY 擅自更改了您文章里的一些格式
比如:给式子搞上了数学公式
比如:您的 boshi 打错了
然后还有一些比如 * 号两侧没打空格导致公示显示错误之类的 OvO
juruo-oier · 2018年8月25日 9:26 下午
万分感谢。
XZYQvQ · 2018年8月25日 8:14 下午
Orz 好文
(说明一下因为似乎要禁 jay 所以我就
#define 好文 tql
了)