template <typename T> structFenwick { int n; std::vector<T> a; Fenwick(int n_ = 0) { init(n_); } voidinit(int n_) { n = n_; a.assign(n, T{}); } voidadd(int x, const T &v) { for (int i = x + 1; i <= n; i += i & -i) { a[i - 1] = a[i - 1] + v; } } T sum(int x){ T ans{}; for (int i = x; i > 0; i -= i & -i) { ans = ans + a[i - 1]; } return ans; } T rangeSum(int l, int r){ returnsum(r) - sum(l); } intselect(const T &k){ int x = 0; T cur{}; for (int i = 1 << int(std::log2(n)); i; i /= 2) { if (x + i <= n && cur + a[x + i - 1] <= k) { x += i; cur = cur + a[x - 1]; } } return x; } };
voidadd(int x, const T &v){ for (int i = x + 1; i <= n; i += i & -i) { a[i - 1] = a[i - 1] + v; } }
作用:在索引 x 位置加上
v,维护树状数组结构。
核心原理:
i = x + 1:树状数组通常从 1 开始存储,因此
x 需要 +1(但 a 仍从
0 开始)。
i += i & -i:跳转到下一个受影响的节点。
a[i - 1] += v:累加 v 到对应位置。
3.
sum(int x):求前缀和
1 2 3 4 5 6 7
T sum(int x){ T ans{}; for (int i = x; i > 0; i -= i & -i) { ans = ans + a[i - 1]; } return ans; }
作用:计算前缀 [0, x-1]
的区间和。
核心原理:
i = x:直接从 x 开始往上累加贡献值。
i -= i & -i:跳转到前一个影响的节点。
ans += a[i - 1]:累加贡献值。
4.
rangeSum(int l, int r):求区间和
1 2 3
T rangeSum(int l, int r){ returnsum(r) - sum(l); }
作用:求 [l, r-1] 区间的和。
计算方式:
sum(r) 计算 [0, r-1] 的前缀和。
sum(l) 计算 [0, l-1] 的前缀和。
区间和= sum(r) - sum(l)。
5.
select(const T &k):二分查找满足前缀和 ≤ k
的最大索引
1 2 3 4 5 6 7 8 9 10 11
intselect(const T &k){ int x = 0; T cur{}; for (int i = 1 << int(std::log2(n)); i; i /= 2) { if (x + i <= n && cur + a[x + i - 1] <= k) { x += i; cur = cur + a[x - 1]; } } return x; }