不能直接用 std::vector 初始化树状数组,因为需手动实现lowbit运算、1-based索引、update/query逻辑;必须用x & -x计算lowbit,初始化需逐调update,tree[0]不用,大小设为n+1。

为什么不能直接用 std::vector 初始化就完事?
手写树状数组不是套个容器就行——核心在于 lowbit 运算和下标偏移处理。C++ 标准库没提供现成的 Fenwick Tree,你得自己管好:数组从 1 开始索引、更新时向上跳、查询时向下拆解。常见错误是把下标当 0 起始来写 update 或 query,结果越界或漏加。
推荐用 std::vector 存值(防 int 溢出),大小设为 n + 1,tree[0] 永远不用。
-
lowbit(x)必须写成x & -x,别用循环或__builtin_ctz(后者依赖平台且不通用) - 初始化时逐个调用
update(i, val),别直接赋值到tree[i]—— 否则父子关系没建起来 - 如果原数组叫
a,建树代码就是:for (int i = 1; i
update 和 query 的边界怎么写才不崩?
两个函数都只操作合法下标:1 到 n。写错 while 条件会导致死循环或跳过根节点。关键点是「更新往上走,查询往下拆」。
-
update(int i, int delta):从i开始,每次加lowbit(i),直到超过n -
query(int i):从i开始,每次减lowbit(i),直到变成 0(注意不是 1) - 区间查询
[l, r]写成query(r) - query(l - 1),这里l - 1可能为 0,但query(0)返回 0 是安全的(因为 while 不进)
示例片段:
void update(int i, int delta) {<br> while (i tree[i] += delta;<br> i += i & -i;<br> }<br>}<br><br>long long query(int i) {<br> long long s = 0;<br> while (i > 0) {<br> s += tree[i];<br> i -= i & -i;<br> }<br> return s;<br>}
单点改 + 区间查 vs 区间改 + 单点查,要不要换结构?
纯手写树状数组默认支持「单点更新 + 前缀和查询」,也就是单点改、区间查。想支持「区间加 + 单点查」,得用差分 + 树状数组,即维护差分数组 d,然后 update(l, v), update(r+1, -v);单点查就是 query(i)。
但如果要「区间加 + 区间和」,单树状数组搞不定,必须上两棵树(常见套路:一棵维护 d[i],一棵维护 i * d[i]),或者直接换线段树。别硬撑——Fenwick 的优势是短小快,不是功能全。
- 确认题型再动手:90% 的 OJ 树状数组题都是单点改 + 区间查
- 差分版本的
update逻辑不变,只是调用方式变:add(l, v); add(r+1, -v); - 别在同一个
tree上混用两种模式,容易符号搞反、下标越界
模板里要不要加 static 或封装成类?
竞赛场景下直接写全局函数+全局 vector 最快;但多人协作或多次复用时,封装成类更稳。重点不是语法糖,而是避免状态残留——比如没清空 tree 就重用,或者 n 变了但 tree.size() 没重置。
- 类封装时,构造函数必须接受
n并 resize:tree.resize(n + 1) - 不要把
lowbit写成成员函数,它只是个 inline 工具,定义成constexpr auto lowbit = [](int x) { return x & -x; };更干净 - 如果用 C++17,可加
[[nodiscard]]在query上,防止忘了接返回值
最简可用类骨架:
struct Fenw {<br> int n;<br> std::vector<long long> tree;<br> Fenw(int sz) : n(sz), tree(sz + 1) {}<br> void update(int i, long long delta) { /* 同上 */ }<br> long long query(int i) { /* 同上 */ }<br>};</long>
真正容易被忽略的是:每次 new 一个 Fenw 实例,都要确保 n 和实际数据范围一致;否则 update 里的 i 判断会提前截断。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











