树状数组
它能做的线段树都能做.
但它快且空间小.
所以学它.
每个数ai管辖的范围为从当前下标开始,向左共(二进制下ai最右的1代表的数)的长度.如24=16+8,就管辖(16,24]的位置.
显然没有任何位置能管辖到0.
要计算前缀和仅需不断减去最低位的1,即x-=x&(-x).
好像^也行,但是影响几乎没有,这也不会成为复杂度瓶颈,所以不这么写.
推柿子的技巧:
为a数组建立差分数组b后,ai=sigma(j=1,i)bj.
然后sigma(i=1,r)ai转为bi相关,发现bi出现了(r-i+1)次.
因此将与bi相关项提出:(-i),与i无关的项即为系数,放到最后乘.
注意add是i*b这个东西加的是同样的一个值:num*初始位置.
因为你是单点加!
注意b是差分数组,而不是原数组.
这样才能将区间加转为单点加,树状数组不能区间加!
所以,推柿子就是得到最基本的元素,然后再找寻它出现的规律?
复习O(n)初始化的方法.
时间戳优化用于初始化多组数据,使用标记以存储当前节点最近一次是被第几组数据使用.
每次操作时判断当前标记和当前组数是否相同,就可以判断这个位置应该是0还是数组内的值.
对于二维的区间加,用容斥相关思想构造差分数组.
不要在struct内写太多东西,写在外面即可.
至于二维差分的理解,假设选择某一行并得到和:
##### //b数组,b[i,j]=a[i,j]+a[i-1,j-1]-a[i,j-1]-a[i-1,j].
# #
#####
#####
+ -#
- +#
#####
##### //这是不完整(左边未到1)的时候对应的选取的a位置.
若为
#####
#
#####
则对应着选了a中的:
-#
+#
#####
而选择了b的第一行时,对应的a为:
+#
#####
#####
所以要想得到(i,j)这个格子的a,要将i*j个b加起来.
而求和所需的是x*y个a的值,对应暴力为:
_rep(now_x,1,x)
_rep(now_y,1,y)
_rep(i,1,now_x)
_rep(j,1,now_y)
ans+=b[i,j]
然后统计每个b[i,j]出现的次数.
例题:
P4514 上帝造题的七分钟 (省选/NOI-)
矩形加和矩形求和,思路上面已讲.
树状数组就此终结.

浙公网安备 33010602011771号