树状数组 (BIT)
单点改 + 区间和,都 O(log n)。秘密在 lowbit(x)=x&(-x):update 沿 +lowbit 向上跳,query 沿 −lowbit 向下跳。
一、每个 c[i] 管哪一段?
下面是 8 个树状数组节点。c[i] 管的区间长度恰好是 lowbit(i),覆盖 [i−lowbit(i)+1, i]。点一个格子看它管的范围。
点任意一个 c[i],看它负责的区间。注意 c[8] 一个人就管了 [1,8]!
二、update 向上跳 / query 向下跳
输入下标 i(1~8):update 要更新所有"管到 i"的节点(绿色,i += lowbit(i));query 把若干段不重叠地拼成前缀和(蓝色,i −= lowbit(i))。
点 update 或 query 看跳跃路径。
每次跳一步,被处理的下标的二进制最低位 1 都会被去掉/进位,所以最多 log n 步 → O(log n)。
三、模板(C++14)
int lowbit(int x) { return x & (-x); }
void update(int i, long long v) { // a[i] += v
for (; i <= n; i += lowbit(i)) c[i] += v;
}
long long query(int i) { // 前缀和 a[1..i]
long long s = 0;
for (; i > 0; i -= lowbit(i)) s += c[i];
return s;
}
// 区间和 [l, r] = query(r) - query(l - 1)
建树:对每个初始值 update(i, a[i]) 即可。要"区间改 + 区间查"请上 线段树;只读区间最值用 ST 表。
小结
① lowbit
x&(-x) 取二进制最低位的 1。c[i] 管 [i−lowbit+1, i],长度 lowbit(i)。② update ↑
i += lowbit(i) 跳到上层,更新所有覆盖它的节点。③ query ↓
i −= lowbit(i) 向下拼前缀和;区间和 = query(r)−query(l−1)。④ 适用
单点改 + 区间和,O(log n);经典应用逆序对(配离散化)。
单点改 + 区间和,O(log n);经典应用逆序对(配离散化)。