树状数组模板

树状数组

在求解区间和的时候,使用前缀和数组,可以通过 sum[r] - sum[l-1]的操作O(1)求取sum[l, r] ,但是如果我们需要修改原数组,那么就得遍历一整个数组修改sum[i],O(n);如果用普通数组,反过来更新的时候O(1),但是求区间和的时间复杂度就变为O(n)了。

引入树状数组, 可 以 在 O ( 可以在O( O( log ⁡ 2 n \log_{2}{n} log2n )的时间复杂度下更新和求取区间和。

个人理解

原始数组A[1–N],树状数组C[1–N]。

标记为灰色的节点实际已被上层覆盖,不占据空间(灰色的结点的值可以通过其父节点和其余节点的值求出,因此省略)

在这里插入图片描述

图片引用至

https://blog.csdn.net/bestsort/article/details/80796531

设节点编号为i,那么该节点维护的值是 [i - lowbit(i) + 1, i] 这个区间的和,C[i]的覆盖长度是lowbit(i)。

C[1] = A[1]		//lowbit(1) = 1
C[2] = A[1] + A[2]		//lowbit(2) = 2
C[3] = A[3]				//lowbit(3) = 1
C[4] = A[1] + A[2] + A[3] + A[4]	//lowbit(4) = 4

二进制模式

在这里插入图片描述

图片引用至 https://blog.csdn.net/bestsort/article/details/80796531
将n个数字构成了一颗满二叉树(歪的,实际省略了一些结点),奇数号结点必为叶节点,偶数号上升,C[i]的覆盖长度是lowbit(i),即i的叶节点孩子的个数是lowbit(i)。实际将区间分成了多个子区间。

  • 更新i的时候,每次从C[i]开始更新,随后对其父节点更新(因为父节点的值是所有子节点的和),直到根节点。在追溯父节点的时候,因为是满二叉树,左右孩子的数量是对称的,都是lowbit(i),则父节点距离结点i的距离(下一个区间的长度)为右孩子数量(即左孩子数量lowbit(i)),加上区间长度后即为下一个区间的右端点也是其父节点。因此寻找父亲的操作为 i+ lowbit(i)。
  • 求区间和的时候我们把先前分割的子区间全部加起来就可以。首先加上C[i],然后跳转到上一个区间,因为C[i]代表 [i - lowbit(i) + 1, i] 的和,所以我们跳转到 i - lowbit(i),即上个区间的右端点。
/*==================================================*\
 | 树状数组
 | INIT: c[]置为0;
 | CALL: add(i, v): 将i点的值加v; sum(i): 求[1, i]的和;
\*==================================================*/
#include<bits/stdc++.h>
using namespace std;

#define typev int // type of res
const int N = 1000;
typev c[N]; // index: 1 ~ N

int lowb(int t){ 
    return t & (-t); 
} 

void add(int i, typev v){ 
    for ( ; i < N; c[i] += v, i += lowb(i)); 
} 

typev sum(int i){ 
    typev s = 0; 
    for ( ; i > 0; s += c[i], i -= lowb(i)); 
    return s; 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值