写在前面:关于树状数组
首先,要灵活运用树状数组,必须要弄懂lowbit.
lowbit 计算的是一个数二进制的最低位。
lowbit(x)=x&-x;
这是为什么呢???
对于一个正数5,它的二进制为00000000 00000000 00000000 00000101 (longlong范围内)
它的负数的原码为 10000000 00000000 00000000 00000101 (在第64位+1)
(二进制的64位为符号位,0为正数,1为负数)
负数的二进制为它的原码的补码(除符号位按位取反后+1)
也就是11111111 11111111 11111111 11111011
所以x&-x就是x的最低位
插一幅图
1.lowbit(x)为x的最低位
2.tr[x]存的是x及以前的lowbit(x)-1位的值的和
3.lowbit(x)还可表示tr[x]所维护的区间长度
一. 一维树状数组
1、最简单的单点修改+区间查询
直接上代码。。。
1 | inline void add(int x,int v) |
2、 稍微难一点点的区间修改+单点查询
这里运用了差分和前缀和的思想。
1 | inline void add(int x,int v) |
3.难一点的区间修改 + 区间查询
(d[i]为差分数组)
我们基于问题2的“差分”思路,考虑一下如何在问题2构建的树状数组中求前缀和:
位置p的前缀和
可变为
那么我们可以维护两个数组的前缀和:
一个数组是 d1[i](d[i]的前缀和)。
另一个数组是 d2[i](d[i]∗i的前缀和)。
查询
位置p的前缀和即: (p + 1) * d1数组中p的前缀和 - d2数组中p的前缀和。
区间[l, r]的和即:位置r的前缀和 - 位置l-1的前缀和。
修改
对于d1数组的修改同单点修改。
对于d2数组的修改也类似,我们给 d2[l] 加上 l * x,给 d2[r + 1] 减去 (r + 1) * x。
解释:修改操作是由查询操作推得的,对于位置l它需要加上一个x,d1已经加上了一个x,d1*(l+1)后
需要再减去l*x,所以在位置l加上l*x,后面依次类推。
上代码:
1 |
|
附一模板题:P3372 【模板】线段树 1
4.区间最值
说明:tr[x]表示的是x-lowbit(x)到 x 的区间最值。
1 |
|
二. 二维树状数组
1、单点修改+区间查询
其实多了一维并没有想象中的那么难,我们可以开两颗树状数组,一颗维护一维的值,另一颗维护前一颗树状数组的值。
可以开一个二维数组,tr[x][y]表示从(1,1)到(x,y)的区间和。
不多说上代码
1 |
|
题目:POJ 1195
2、区间修改+单点查询
对与区间修改单点查询,我们还沿用一维的套路——差分。
由二维前缀和:sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j]可推的。
d[i][j](二维差分数组)=a[i][j]-a[i-1][j]-sum[i][j-1]+a[i-1][j-1]
来一道题目:POJ2155Matrix
1 |
|
3.区间修改+区间查询
(d[i][j]为差分数组)
$$ sum[i][j]=\sum_{x=1}^{i}\sum_{y=1}^{j}\sum_{a=1}^{x}\sum_{b=1}^{y}d[a][b] $$
整理可得:
$$ sum[i][j]=\sum_{x=1}^{i}\sum_{y=1}^{j}d[x][y](i+1-x)(j+1-y) $$
$$ sum[i][j]=(i+1)*(j+1)\sum_{x=1}^{i}\sum_{y=1}^{j}d[x][y]-(i+1)\sum_{x=1}^{i}\sum_{y=1}^{j}d[x][y]*y-(j+1)\sum_{x=1}^{i}\sum_{y=1}^{j}d[x][y]*x+\sum_{x=1}^{i}\sum_{y=1}^{j}d[x][y]xy$$
由上式可的我们须用四个树状数组分别记录d[i][j]*i,d[i][j]*j,d[i][j]ij,和d[i][j]。
1 |
|
本文作者:
syrsteven
最后更新: 2023年09月02日 04:06:21
本文链接: http://syrsteven.github.io/post/30ce1405.html
版权声明: 本作品采用 CC BY-NC-SA 4.0 许可协议进行许可,转载请注明出处!
最后更新: 2023年09月02日 04:06:21
本文链接: http://syrsteven.github.io/post/30ce1405.html
版权声明: 本作品采用 CC BY-NC-SA 4.0 许可协议进行许可,转载请注明出处!