浅谈树状数组
概念:
定义:
树状数组是一种结合了树的思想,常用来处理前缀问题(如前缀最大/最小值,前缀和)的一种数据结构,区查和单修时间复杂度都为 \(\log(n)\)。
(右图括号中的值表示原序列中该数的值,未打括号的表示下标)
这一张图可以很好地展示一维树状数组的结构及其维护信息的原理。令该树状数组为 \(bit\),\(c\) 为数组的前缀和,容易发现,$$c[1] = bit[1]$$ $$c[2] = bit[2]$$$$c[3] = bit[2] + bit[3]...$$ $$c[7] = bit[7] + bit[6] + bit[4]$$。
结合图观察,通过树状数组求前缀和就是将某个区间拆成树状数组里的若干段的规律就是将该数进行二进制拆分,每一次取出 \(01\) 串中最靠右的 \(1\) 表示的数作为树状数组中的下标,然后加起来即可。
例如 \(A[5]\),以二进制表示就是 \(A[0101]\) 拆分过后就是 \(bit[0100] 和bit[0001]\),分别为 \(bit[4]和bit[1]\) 因此 \(A[5] = bit[4] + bit[1]\)。
lowbit:
那么如何每次准确地取出 \(01\) 串中最靠右的一个 \(1\) 呢?这里需要对当前数取负后再与上当前数即可。
在计算机中,负数用补码表示,补码等于正数用二进制表示后每一位取反后再加一。如 \(5\) 用二进制表示就是 \(0101\),那么 \(-5\) 用二进制表示就是 \(0101\) 按位取反,得到 \(1010\),然后加一有 \(1011\),这就是 \(-5\)的二进制表示法。为了取出最后一位 \(1\),还应该将这个数与 \(5\) 进行与运算,得到 \(0001\),这样就顺利取出了 \(01\) 串中的最后一位 \(1\)。在树状数组的实现中,我们常用这样一个 \(lowbit\) 函数实现这样的操作:
还可以采用异或实现 \(lowbit\) 此处不展开叙述,读者自行探索。
一维树状数组:
单修区查:
单修:
假如我们对原序列中第十一位进行修改,那么树状数组中,包含原序列第十一位的数的信息的所有结点都需要改变,这就像从第十一位拉一条水平方向的横线(如右图),被这条横线所切到的所有结点的值都要被改变(红圆标注)。在这种情况下,树状数组中下标为 \(11,12,16\) 的结点都需要被改变。
观察三数,转化为二进制后分别为 \(1011,1100,10000\),可以发现,每一个数都相当于前一个数在它最靠右的 \(1\) 的位置加上 \(1\) 后所得到的数。这里仍然要用到 \(lowbit\)
实现如下:
区查:
按照介绍 \(lowbit\) 时所介绍的求前缀和的思想进行编程:
求得前缀和,进行区间和查询也就易如反掌了。
区修单查:
区修:
对于树状数组的区间修改,要运用差分的思想,一个数组的差分数组和它的前缀和是互逆的。
容易发现,差分数组的前缀和就是原数组的数,前缀和的差分数组就是原数组。
而差分数组的区间修改是将 \(cf[l]+k,cf[r+1]−k\) (设让 \([l,r]\) 里的每个数加上 \(k\),\(cf\) 为原数组的差分数组)
对于这道题,我们不再在原数组上建树状数组了,改在差分数组上建树状数组。
单查:
每次区间修改,就对 \(cf[l]+k,cf[r+1]−k\) ,查询每个数,即求 \([1,x]\) 的前缀和。
\(query,add\) 函数不变。
区修区查:
区修:
同上。
区查:
这里就需要稍微推一下柿子了。设原序列为 \(A\)。
这个柿子的结果就是区间和。展开得到:
化简后得:
因此只需要维护两个前缀和,分别是 \(bit[i]和bit[i] * (i - 1)\)。
二维树状数组:
单修区查:
其实跟一维的大同小异,这里就放代码了:
在进行区间矩阵求和时,还要运用到容斥定理,如下图(找别人薅的
大正方形减去两个小的长方形,加上小正方形即为红框区间内的答案。
区修单查:
区修:
一位的区修依赖差分数组,那么二维的区修就依赖差分矩阵。
有如下矩阵
想进行区域修改,得到
那么在差分矩阵里,就应该是这样的
代码如下:
单查:
如一维一样,统计前缀和即可。
区修区查:
区修:
同上。
区查:
令 \(a\) 为差分矩阵,则有
类比一维树状数组,发现 \(a[x][y]\) 出现了 \((x - i + 1) * (y - j + 1)\) 次。
所以可以化简,得到:
显而易见,需要维护 \(a[i][j] 、a[i][j]∗j、a[i][j]∗i、a[i][j]∗i∗j\)
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)