A Fenwick tree (binary indexed tree) stores partial sums over power-of-two blocks: node (0-indexed form below) covers . Point update and prefix query are , with the smallest constant and code of any dynamic range structure. It works for any group operation (sum, XOR, modular product of units). Min/max work only for prefixes with monotone updates.

Implementation (0-indexed, half-open)

template<class T> struct Fenwick {
    int n; vector<T> t;
    Fenwick(int n = 0) : n(n), t(n) {}
    Fenwick(const vector<T>& a) : n(sz(a)), t(a) {           // O(n) build
        for (int i = 0; i < n; i++) { int j = i | (i + 1); if (j < n) t[j] += t[i]; }
    }
    void add(int i, T v) { for (; i < n; i |= i + 1) t[i] += v; }
    T sum(int r) const { T s{}; for (; r > 0; r &= r - 1) s += t[r - 1]; return s; }   // [0, r)
    T sum(int l, int r) const { return sum(r) - sum(l); }
    int lowerBound(T k) const {                  // largest p with sum(p) <= k (values >= 0)
        int p = 0;
        for (int pw = n ? 1 << __lg(n) : 0; pw; pw >>= 1)
            if (p + pw <= n && t[p + pw - 1] <= k) p += pw, k -= t[p - 1];
        return p;                                // so the (k+1)-th unit (0-indexed k) is at index p
    }
};

Descent: with counts as values, lowerBound(k) returns the index of the -th element (0-indexed) of a multiset over in one walk. That’s the fast order-statistics structure when values are known up front.

Range operations

NeedTrick
range add, point queryFenwick over the difference array: add(l, v); add(r, -v), value = sum(i + 1)
range add, range sumtwo Fenwicks , over and :
point update, prefix max with only increasing updatest[i] = max(t[i], v) walks
range max with arbitrary updatesnot a Fenwick job: segment tree
-th element, rankcounts + descent
inversions, “smaller elements to the left”scan, query, add (after compression)
template<class T> struct RangeFenwick {                       // a[l, r) += v, sum over [l, r)
    Fenwick<T> d, e; RangeFenwick(int n) : d(n + 1), e(n + 1) {}
    void add(int l, int r, T v) { d.add(l, v); d.add(r, -v); e.add(l, v * l); e.add(r, -v * r); }
    T prefix(int r) const { return d.sum(r) * r - e.sum(r); }
    T sum(int l, int r) const { return prefix(r) - prefix(l); }
};

Intermediate values reach about : use __int128 or a modint if they can overflow.

2D and higher

struct Fenwick2D {                                            // point add, rectangle sum, 0-indexed half-open
    int n, m; vector<vector<ll>> t;
    Fenwick2D(int n, int m) : n(n), m(m), t(n, vector<ll>(m)) {}
    void add(int x, int y, ll v) { for (int i = x; i < n; i |= i + 1) for (int j = y; j < m; j |= j + 1) t[i][j] += v; }
    ll sum(int x, int y) const { ll s = 0; for (int i = x; i > 0; i &= i - 1) for (int j = y; j > 0; j &= j - 1) s += t[i - 1][j - 1]; return s; }
    ll sum(int x1, int y1, int x2, int y2) const { return sum(x2, y2) - sum(x1, y2) - sum(x2, y1) + sum(x1, y1); }
};
  • 2D range add + range sum: four 2D Fenwicks storing , , , of the 2D difference array; then with the right shifts. .
StructureBuildQueryUpdateMemory
2D Fenwick
2D segment tree (rows of column-segment-trees)
2D segment tree, sparse (nodes allocated on demand)—

No lazy propagation in 2D

Range updates on a 2D segment tree with lazy propagation don’t work in general — a lazy tag on a row node can’t be pushed into its column tree consistently (there’s no single column-tree node to tag). Use the four-Fenwick range-update trick above (sums only), a sqrt decomposition, or offline processing instead.

  • dimensions with compile-time sizes: a recursive template FenwickND<T, N, Ns...> holding N+1 lower-dimensional Fenwicks; per operation and memory.
  • Everything known offline, no updates: compress both coordinates to and use a dense 2D structure directly — the simplest option when it applies.
  • Large coordinates, offline: sort points and queries by , sweep with a 1D Fenwick over compressed (Offline 2D Queries); time, memory — usually the right answer whenever queries can be sorted.
  • Large coordinates, online, point updates: a Fenwick of sorted vectors, each with its own small Fenwick (compress per node, offline insertion of the set of per node), per operation with memory. Or a Fenwick of order-statistic trees.
  • Online rectangle queries, no updates: a persistent segment tree where version contains all points with ; a rectangle query is the difference of two versions, per query, memory.
  • Online, with insertions: a k-d tree, per rectangle query, memory.

On trees

  • Subtree sum with point updates: Euler tour turns subtrees into ranges .
  • Path sum from the root with point updates: add at and at ; the root-to- sum is a prefix sum up to . The – path is .
  • Path add, point query: add at both endpoints and subtract at the LCA and its parent; the value of is the subtree sum of .

Notes

  • Use the 1-indexed variant (i += i & -i) if you prefer; the 0-indexed i |= i + 1 / r &= r - 1 form avoids off-by-one shifts with half-open ranges.
  • Fenwick over values replaces std::set for rank queries and is 5-10x faster.
  • XOR Fenwick: += becomes ^=, and range XOR is sum(r) ^ sum(l).
  • Persistence: querying old versions of a Fenwick is awkward (each add touches scattered nodes, so naive path-copying doesn’t save anything) — reach for a persistent segment tree instead whenever historical queries are needed.