(网上的许多博客讲解树状数组的时候都把他当做“一棵树”来讲解,但我认为把他当成“数组”来理解反而更加清晰易懂)
先引入一下 lowbit: 设有一个数 x, 那么 lowbit(x) 就是把 x 用二进制表示后,从右到左第一个 1 所表示的权值。例如,lowbit(5)=lowbit(101)=1,lowbit(10)=lowbit(1010)=2,lowbit(8)=8. (lowbit 也表示最大能整除 x 的 2 的幂)
树状数组具体实现:我们设原数组为 a, 树状数组为 c. 其中,ci 表示 a[i]+a[i−1]+...+a[i−lowbit(i)+1], 即从 ai 开始(包括 ai)的前面的 lowbit(i) 个数的和。
—— 如何查询 a1+a2+...+ax 呢?
while(x){
ans += c[x];
x -= lowbit(x);
}
至于如何查询 [l,r] 的和,只需用 query(r)−query(l−1) 即可。
—— 要给 ax 加上 k,c 数组需要怎么修改呢?
先给出做法:
while(x <= N){
c[x] += k;
x += lowbit(x);
}
当 ax 修改时,我们维护的 c 数组也要相应的修改——要且仅要修改那些包含了 ax 的 ci. 现在,问题转化成了,我们怎样找全包含了 ax 的 ci 呢?
首先,我们约定,“cn 覆盖 cm ” 表示 cm 所维护的区间 ∈ cn 所维护的区间。例如,c8(它维护a8+a7+a6+a5) 覆盖 c6(它维护a6+a5).
- 性质 1:若 ca 覆盖 cb, 且 cb 覆盖 cn, 那么 ca 覆盖 cn。(a≠b≠n)(这是比较显然的,结合“覆盖”的定义不难想明白)
- 命题 1:c[x+lowbit(x)] 覆盖 c[x]
证明:
首先,不难得出,lowbit(x+lowbit(x))>lowbit(x).
⇒lowbit(x+lowbit(x))≥lowbit(x)×2
设 lowbit(x+lowbit(x))=lowbit(x)×2+k (k≥0)
c[x]=a[x]+...+a[x−lowbit(x)+1]
c[x+lowbit(x)]=a[x+lowbit(x)]+...+a[a−lowbit(x)+1−k]
显然,c[x+lowbit(x)] 覆盖 c[x].
- 命题 2 : 若 x<y<x+lowbit(x) (lowbit(x)>1), 则 c[y]∩c[x]=∅(这里,c[y]∩c[x] 表示 c[y] 与 c[x] 所维护的区间的交集)
证明:
设 y=x+b. 那么,有 1≤b<lowbit(x), 同时,可知 lowbit(y)=lowbit(b) (这里需要想一想,不难).
考察 c[y] 所维护的区间的最左端 y−lowbit(y)+1. y−lowbit(y)+1=x+b−lowbit(b)+1>x
所以,c[y]∩c[x]=∅
有了以上的命题和性质,前面单点修改的代码的正确性,也就不难证明了。
当问题可以转化为对一个序列进行单点修改,区间查询时,我们就可以去维护树状数组而非直接维护这个序列。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理