专栏文章
P3374 【模板】树状数组 1 题解
P3374题解参与者 11已保存评论 12
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 12 条
- 当前快照
- 1 份
- 快照标识符
- @mipt1bzy
- 此快照首次捕获于
- 2025/12/03 17:28 3 个月前
- 此快照最后确认于
- 2025/12/03 17:28 3 个月前
树状数组是一种支持单点修改,区间查询的精巧的数据结构,通常用于维护满足结合律和可差分的运算和信息。又称二叉索引树(Binary Index Tree)、Fenwick Tree。
下面这张图展示了树状数组的原理(来源:OI-Wiki)。
其中 表示以 为右端点,长度为 的区间的和。
表示的是 在二进制表示下,最低位的 的权值。
例如, 在二进制表示下为 ,加粗的就是最低位的 ,它的权值是 ,因此 。
再例如, 在二进制表示下为 ,最低位的 的权值为 ,因此 。
根据位运算知识,可以得到lowbit(x) = x & -x,其中&为按位与运算。
如果一个数减去自己的 ,得到的数再减去自己的 ,不断重复,最终这个数一定会变成 。
例如 。
那么我们要计算 的和,就只需要求 即可。观察上图,看看是不是这样。
由此我们可以得到查询 的代码:
CPPint query(int x)
{
int ans = 0;
while(x > 0)
{
ans += c[x];
x -= lowbit(x);
}
return ans;
}
可以发现,树状数组通过将一段数划分成 段数的和,从而能够实现高效的查询操作。
如果要求任意一段区间 的和,可以借助前缀和的思想,用 的和减去 的和,即
query(r) - query(l-1)。这也说明树状数组可以当成一个支持修改的前缀和来用。如果要将 加上一个数 该如何处理?观察包含 的区间,只有 , 和 。那么就只需要将 , 和 都加上 即可。而 ,。也就是说,在树状数组中,一个结点 的父亲是 。由此我们可以得到将 加上 的代码:
CPPvoid update(int x, int k)
{
while(x <= n)
{
c[x] += k;
x += lowbit(x);
}
}
显然,修改操作的时间复杂度也为 。
以下是一份经过封装的极简树状数组代码,可以通过本题。
CPP#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5;
struct BIT{ //树状数组
int c[N], lowbit(int x){return x & -x;}
void update(int x, int k){while(x < N) c[x] += k, x += lowbit(x);}
int query(int x){int s = 0; while(x) s += c[x], x -= lowbit(x); return s;}
} t;
long long n, m;
signed main(){
cin.tie(nullptr) -> sync_with_stdio(false);
cin >> n >> m;
for(int i=1, x; i<=n; i++) cin >> x, t.update(i, x);
while(m --> 0){
int op, x, y; cin >> op >> x >> y;
if(op == 1) t.update(x, y);
if(op == 2) cout << t.query(y) - t.query(x - 1) << "\n";
}
return 0;
}
推荐继续阅读我的 树状数组小记,包含更多树状数组的高级应用。
相关推荐
评论
共 12 条评论,欢迎与作者交流。
正在加载评论...