一些懒人由于觉得线段树难写的一逼,于是发明了树状数组。于是大佬们拿它去卡常。啥树套树的你套个树状数组,常数就小了下来,就快了好几倍。苦逼写线段树的同学还过不去了。
所以本着不卡别人也不被别人卡的思想,我们来学习一波树状数组。首先树状数组比线段树优秀的一点就是它的空间就小一半。我们只用开一个大小为n的数组,存的样子大概像这样:
什么意思呢?看到这个树形结构了么?
不好说明,举个例子:2号点存1-2的和;8号点存1-8的和;10号点存9-10的和;13号点存13-13的和;5号点存5-5的和。
然后我们就可以做到(单点修改,区间求和)
我们来看一道题:P3374 【模板】树状数组 1
我们再引入一个概念: lowbit(x)=x l o w b i t ( x ) = x 的2的最大次幂。举个例子: lowbit(6)=2(6=2∗3);lowbit(12)=4(12=4∗3) l o w b i t ( 6 ) = 2 ( 6 = 2 ∗ 3 ) ; l o w b i t ( 12 ) = 4 ( 12 = 4 ∗ 3 ) 。
然后我们就可以 logn l o g n 求前缀和了,查询时每次 −lowbit(x) − l o w b i t ( x ) ,然后把那一个位置里的和加入 ans a n s 里。再举个例子,求7的前缀和:
7——6(7−lowbit(7))——4(6−lowbit(6))——0(4−lowbit(4))
7
—
—
6
(
7
−
l
o
w
b
i
t
(
7
)
)
—
—
4
(
6
−
l
o
w
b
i
t
(
6
)
)
—
—
0
(
4
−
l
o
w
b
i
t
(
4
)
)
到0就结束。
从图里可以发现,我们正好把每个7前面的数都加了一遍。原理就是 lowbit(x) l o w b i t ( x ) 其实表示 x x 的管理范围。
然后考虑修改:修改时每次,然后修改那一位置的值,我们就可以把所有实际管理 x x 的格子修改掉了。可以自己手动模拟一下,我就不举例子了。
然后前缀和相减,我们很容易可以过这道题。
接着我们在来看一道题:P3368 【模板】树状数组 2
诶,这道题要求我们(区间修改,单点查询)。
看似刚刚那种方法已经无解了,显然无法区间修改,我们每一次单点修改复杂度就是的,要是一段区间的话岂不是coldcold。那我们要想个办法把区间修改变为单点修改。于是很容易会想到差分。然后我们很惊奇地发现查询操作也很简单了。考虑我们以前差分后统计答案的方法,其实就是这个点前面的前缀和。这不是可以用树状数组轻松解决么?
举个例子:[3,6] +2,3号点+2,7号点-2。
查询4号点,求四号点前缀和。
用我们刚刚那种方法维护数组即可。
我们树状数组就讲完了,是不是很简单呢,时间上吊打线段树。
诶,不对啊,你还没讲 lowbit l o w b i t 怎么求。
啪!!!
哦~
其实你可以脑补出一种方法,据说考场上各路大佬现推公式都可以求。只要时间控制在 O(1) O ( 1 ) 即可。这里提供一种简单方法:
lowbit(x)= (-x)&x;