单点加与区间求和交替:树状数组按 lowbit 把前缀和拆成 O(log n) 段,修改沿链更新、查询两次前缀和相减,O(n + m log n)。
OJ: roj
题目 ID: 1535
难度:普及-
标签:树状数组前缀和python
创建: 2026-09-30 16:42
更新: 2026-10-06 00:35
后置题目
luogu P3372【模板】线段树 1B 的双树状数组解法沿用 A 教的 Fenwick lowbit 链单点加与「两次前缀和相减」框架,区间和仍用 prefix(r)-prefix(l-1) 求;只是把单点加扩成差分边界的两次 add,并加第二棵 Fenwick 维护 i*d[i] 还原区间和。luogu P3368【模板】树状数组 2B 复用 A 教的沿 lowbit 向上 add、向下 prefix 的 Fenwick 模板,只是把维护对象从原数组换成差分数组,从而把单点加/区间查对偶成区间加/单点查。luogu P1908逆序对B 的 main.py 直接复用 A 教的 i-=lowbit 前缀和与 i+=lowbit 单点加,把 A 的区间和树状数组换成语义为计数的值域桶,再叠加 A 未教的离散化与二维偏序统计。luogu P1250种树B 的贪心每次都要问区间已有树数,直接复用 A 教的树状数组点加+两次前缀和相减的区间和维护,再叠加按右端点排序、从右补树的贪心流程
题目描述
给定长度为
思路
树状数组维护前缀和:单点修改沿
参考代码
Python 版:
python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-09-30 16:50
# update_at: 2026-09-30 16:50
import sys
from itertools import accumulate
def prefix_sum(bit: list[int], i: int) -> int:
"""前缀和:累加 bit 中管辖 [1..i] 的 O(log n) 个结点。"""
s = 0
while i:
s += bit[i]
i -= i & -i # lowbit:去掉最低位的 1,沿父链上跳
return s
def add(bit: list[int], i: int, delta: int) -> None:
"""单点加:把 delta 累加到第 i 个元素,并更新所有管辖 i 的结点。"""
while i < len(bit):
bit[i] += delta
i += i & -i # lowbit:跳到把本段纳入区间的上层结点
def solve() -> None:
data = iter(map(int, sys.stdin.buffer.read().split()))
n, m = next(data), next(data)
values = [next(data) for _ in range(n)]
# 树状数组建树:pre 是前缀和,bit[i] 恰为 (i-lowbit(i), i] 的区间和,一次推导式 O(n) 建好
pre = list(accumulate(values, initial=0))
bit = [0] + [pre[i] - pre[i - (i & -i)] for i in range(1, n + 1)]
out: list[str] = []
for _ in range(m):
k, a, b = next(data), next(data), next(data)
if k == 1: # k=1:第 a 个数加 b
add(bit, a, b)
else: # 其余 k 都是求子数列 [a,b] 的连续和(样例 k=0,真实数据 k=2)
out.append(str(prefix_sum(bit, b) - prefix_sum(bit, a - 1)))
print('\n'.join(out))
if __name__ == "__main__":
solve()C++ 版(同一算法):
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-06 00:35
* update_at: 2026-10-06 00:35
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 1000005; // 真实数据 n 最大到 1e6
int n, m;
ll bit[MAXN]; // bit[i] 存区间 (i - lowbit(i), i] 的元素和
// 返回 x 的最低位 1 代表的值
inline int lowbit(int x) {
return x & (-x);
}
// 在位置 i 加上 v
void add(int i, ll v) {
for (; i <= n; i += lowbit(i))
bit[i] += v;
}
// 求前缀和 [1, i]
ll query(int i) {
ll res = 0;
for (; i > 0; i -= lowbit(i))
res += bit[i];
return res;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
ll x;
cin >> x;
add(i, x); // 树状数组单点插入建树
}
for (int i = 1; i <= m; ++i) {
int k, a, b;
cin >> k >> a >> b;
if (k == 1) {
add(a, b); // 单点加
} else {
cout << query(b) - query(a - 1) << '\n'; // 区间求和
}
}
return 0;
}