专栏文章
【笔记】树状数组
算法·理论参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @miow4qgv
- 此快照首次捕获于
- 2025/12/03 02:07 3 个月前
- 此快照最后确认于
- 2025/12/03 02:07 3 个月前
树状数组可以高效率地查询和维护数列的前缀和(或区间和),支持单点修改和区间查询。
根据仍以正整数关于 的不重复次幂的唯一分解性质,可以尝试按照二进制拆分,具体如下:
若一个正整数 可以被“二进制分解”成 ,其中 ,则可以将 分解为不超过 个小区间:
- 长度为 的小区间
- 长度为 的小区间
- 长度为 的小区间
其中,若区间结尾为 ,则区间长度为 的二进制分解下最小的 的次幂,即
lowbit(R)。由二进制得
CPPlowbit(x)=x&(~x+1)=x&(-x)。因为取反后,末尾的所有 变为 ,最低位的 变为了最低位的 ,此时再对其加 ,会一直向前进位,直至最低位的 ,此时再进行按位与,自然只有最低位的 被保留。当然,因为补码情况下 -x=~x+1,所以还可以进一步简化。int lowbit(int x) {return x&(-x);}
int main()
{
cin >> x;
for (int i = x; i; i -= lowbit(i))
//分成的每一个区间为 [i-lowbit(i)+1, i]
}
树状数组基于此,可以维护序列前缀和。对于给定的序列 ,我们令数组 的第 项 保存序列 的区间 中的所有数之和,即 。
由此,我们写出树状数组的查询前缀和代码。
CPPll query(int x)
{//查询[1,x]区间和
ll ans=0;
for(;x>0;x-=x&(-x)) ans += a[x]; //拆区间,计算和
return ans;
}
事实上,树状数组中,结点 的父亲为 。因此在单点修改中,我们只需要沿着单点(仅管理 的叶节点)向根节点的路径修改即可,直到跳到数组长度之外为止。
由此写出树状数组的单点修改代码。
CPPvoid add(int x, int y)
{//将 a[x] 加上 y
for (;x<=n;x+=x&(-x)) a[x]+=y;//不断修改更新并跳向父亲,直至超出数组长度n
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...