【模板】树状数组(7.26)
update:二维树状数组的实现
一.概述
树状数组,是一个区间查询和单点修改复杂度都为log(n)的数据结构。主要用于查询任意两点之间的所有元素之和。
树状数组的题多半要转化,利用差分数组,可以将区间修改变为单点修改,单点查询变为区间查询(即前缀和)。只要满足单点修改,我们总能用树状数组来维护前缀和。
b i t [ i ] bit[i] bit[i]数组可以看作一个树形结构,它满足以下三个性质:
- 每个内部节点 b i t [ x ] bit[x] bit[x]保存以它为根的子树中所有叶节点的和
- 每个内部节点 b i t [ x ] bit[x] bit[x]的子节点个数等于 l o w b i t ( x ) lowbit(x) lowbit(x)的位数
- 除树根外,每个内部节点bit[x]的父节点是bit[x+lowbit(x)]
- 树的深度为 O ( l o g N ) O(logN) O(logN)
- b i t [ x ] bit[x] bit[x]表示区间 [ x − l o w b i t ( x ) + 1 , x ] [x-lowbit(x)+1,x] [x−lowbit(x)+1,x]
个人认为它比较数学化,利用了数的性质(尤其是二进制)。
二.例题
- 模拟类
- 计数类(偏数学)
一维树状数组:
1.单点修改,区间查询
2.区间修改,区间查询
引入差分概念:
c [ i ] = a [ i ] − a [ i − 1 ] c[i]=a[i]-a[i-1] c[i]=a[i]−a[i−1]
那么某个元素的值其实等于
a
[
i
]
=
∑
i
=
1
n
c
i
a[i]=\sum_{i=1}^{n}c_{i}
a[i]=∑i=1nci
那么区间和呢?
我们可以知道
s u m [ x ] = ∑ i = 1 x ∑ j = 1 i c [ i ] = ∑ i = 1 x c [ i ] ∗ ( x − i + 1 ) = ∑ i = 1 x c [ i ] ∗ ( x + 1 ) − c [ i ] ∗ i sum[x]=\sum_{i=1}^{x}\sum_{j=1}^{i}c[i]=\sum_{i=1}^{x}c[i]*(x-i+1)=\sum_{i=1}^{x}c[i]*(x+1)-c[i]*i sum[x]=∑i=1x∑j=1ic[i]=∑i=1xc[i]∗(x−i+1)=∑i=1xc[i]∗(x+1)−c[i]∗i
因此我们需要维护两个值:差分数组 c [ i ] c[i] c[i] 和 b [ i ] = c [ i ] ∗ i b[i]=c[i]*i b[i]=c[i]∗i 。
二维树状数组:
https://blog.csdn.net/qq_35885746/article/details/89247993
1. 单点修改,区间查询
思路:
b
i
t
[
i
]
[
j
]
bit[i][j]
bit[i][j]表示
(
i
−
l
o
w
b
i
t
(
i
)
+
1
,
j
−
l
o
w
b
i
t
(
j
)
+
1
)
(i-lowbit(i)+1,j-lowbit(j)+1)
(i−lowbit(i)+1,j−lowbit(j)+1)到
(
i
,
j
)
(i,j)
(i,j)的子矩阵和,sum函数显然很好写:
关键的是update函数。仔细思考: i i i, j j j的父节点分别是 i + l o w b i t ( i ) − 1 i+lowbit(i)-1 i+lowbit(i)−1 和 j + l o w b i t ( j ) − 1 j+lowbit(j)-1 j+lowbit(j)−1,我们要更新的 x x x, y y y必然都是 i i i, j j j的祖先节点,所以用二重循环遍历祖先节点即可。
查询操作是 O ( 1 ) O(1) O(1):
总时间复杂度是 O ( l o g n ∗ l o g n ∗ t ) O(logn*logn*t) O(logn∗logn∗t),t是操作总数
2.区间修改,单点查询
思路:这里维护的数组值是差分数组的前缀和。
我们可以令差分数组
d
[
i
]
[
j
]
=
a
[
i
]
[
j
]
−
a
[
i
−
1
]
[
j
]
−
a
[
i
]
[
j
−
1
]
+
a
[
i
−
1
]
[
j
−
1
]
d[i][j]=a[i][j]- a[i-1][j]-a[i][j-1]+a[i-1][j-1]
d[i][j]=a[i][j]−a[i−1][j]−a[i][j−1]+a[i−1][j−1]。
然后神奇的发现:
a
[
i
]
[
j
]
=
∑
x
=
1
i
∑
y
=
1
j
d
[
x
]
[
y
]
a[i][j]=\sum_{x=1}^{i}\sum_{y=1}^{j}d[x][y]
a[i][j]=∑x=1i∑y=1jd[x][y]
至于证明,显然成立(虽然这个式子比较难想)
然后考虑 d [ i ] [ j ] d[i][j] d[i][j] 数组的变化,发现一次区间修改相当于把四个点的d值进行修改。update函数把sum累加,正好就是点的变化。
3.区间修改,区间查询
思路:仍然维护d数组,修改函数同上。
下面讨论如何计算区间的和:
s
u
m
[
x
]
[
y
]
=
∑
i
=
1
x
∑
j
=
1
y
a
[
i
]
[
j
]
=
∑
i
=
1
x
∑
j
=
1
y
d
[
i
]
[
j
]
∗
(
x
−
i
+
1
)
∗
(
y
−
j
+
1
)
=
∑
i
=
1
x
∑
j
=
1
y
d
[
i
]
[
j
]
∗
(
x
y
+
x
+
y
+
1
)
−
d
[
i
]
[
j
]
∗
i
∗
(
y
+
1
)
−
d
[
i
]
[
j
]
∗
j
∗
(
x
+
1
)
+
d
[
i
]
[
j
]
∗
i
∗
j
sum[x][y]=\sum_{i=1}^{x}\sum_{j=1}^{y}a[i][j]=\sum_{i=1}^{x}\sum_{j=1}^{y}d[i][j]*(x-i+1)*(y-j+1)=\sum_{i=1}^{x}\sum_{j=1}^{y}d[i][j]*(xy+x+y+1)-d[i][j]*i*(y+1)-d[i][j]*j*(x+1)+d[i][j]*i*j
sum[x][y]=∑i=1x∑j=1ya[i][j]=∑i=1x∑j=1yd[i][j]∗(x−i+1)∗(y−j+1)=∑i=1x∑j=1yd[i][j]∗(xy+x+y+1)−d[i][j]∗i∗(y+1)−d[i][j]∗j∗(x+1)+d[i][j]∗i∗j
那么我们要开四个树状数组,分别维护:
d [ i ] [ j ] , d [ i ] [ j ] ∗ i , d [ i ] [ j ] ∗ j , d [ i ] [ j ] ∗ i ∗ j d[i][j],d[i][j]*i,d[i][j]*j,d[i][j]*i*j d[i][j],d[i][j]∗i,d[i][j]∗j,d[i][j]∗i∗j
这样就可以解决上述问题了
__EOF__

本文链接:https://www.cnblogs.com/cqbzly/p/17530414.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」