树状数组

(网上的许多博客讲解树状数组的时候都把他当做“一棵树”来讲解,但我认为把他当成“数组”来理解反而更加清晰易懂)

先引入一下 lowbit: 设有一个数 x, 那么 lowbit(x) 就是把 x 用二进制表示后,从右到左第一个 1 所表示的权值。例如,lowbit(5)=lowbit(101)=1,lowbit(10)=lowbit(1010)=2,lowbit(8)=8. (lowbit 也表示最大能整除 x2 的幂)

树状数组具体实现:我们设原数组为 a, 树状数组为 c. 其中,ci 表示 a[i]+a[i1]+...+a[ilowbit(i)+1], 即从 ai 开始(包括 ai)的前面的 lowbit(i) 个数的和。

查询

—— 如何查询 a1+a2+...+ax 呢?

while(x){
    //x 代表着“现在还有 a_1, a_2...a_x 这些数没有加到 ans 里”
    ans += c[x];//运行完这一行表示 a_x 前 lowbit(x) 个数(包括 a_x)已经加到 ans 里了,
    x -= lowbit(x);//根据上一行的解释和 x 的定义,x -= lowbit(x) 是显然的
}

至于如何查询 [l,r] 的和,只需用 query(r)query(l1) 即可。

单点修改

—— 要给 ax 加上 kc 数组需要怎么修改呢?
先给出做法:

while(x <= N){//这里, N 表示原数组的大小
    c[x] += k;
    x += lowbit(x);
}

证明其正确性:

ax 修改时,我们维护的 c 数组也要相应的修改——要且仅要修改那些包含了 axci. 现在,问题转化成了,我们怎样找全包含了 axci 呢?

首先,我们约定,“cn 覆盖 cm ” 表示 cm 所维护的区间 cn 所维护的区间。例如,c8(a8+a7+a6+a5) 覆盖 c6(a6+a5).

  • 性质 1:若 ca 覆盖 cb, 且 cb 覆盖 cn, 那么 ca 覆盖 cn(abn)(这是比较显然的,结合“覆盖”的定义不难想明白)
  • 命题 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 (k0)
    c[x]=a[x]+...+a[xlowbit(x)+1]
    c[x+lowbit(x)]=a[x+lowbit(x)]+...+a[alowbit(x)+1k]
    显然,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. 那么,有 1b<lowbit(x), 同时,可知 lowbit(y)=lowbit(b) (这里需要想一想,不难).
    考察 c[y] 所维护的区间的最左端 ylowbit(y)+1. ylowbit(y)+1=x+blowbit(b)+1>x
    所以,c[y]c[x]=

有了以上的命题和性质,前面单点修改的代码的正确性,也就不难证明了。


当问题可以转化为对一个序列进行单点修改,区间查询时,我们就可以去维护树状数组而非直接维护这个序列。

posted @   Jiayn  阅读(96)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示