← SciSim / Computer Science

[F] Fenwick Tree (BIT)

Binary Indexed Tree | Prefix Sums | O(log n) Update and Query

Standard Undergraduate

Section 1 -- Interactive Simulation

Speed:
0
Operations
0
Comparisons
0/0
Step
16
n
n: 16     
BIT array: bit[1..n], 1-indexed
UPDATE(i, delta): // add delta to position i
  while i <= n: bit[i] += delta; i += i & (-i)
PREFIX-SUM(i): // sum of A[1..i]
  s = 0
  while i > 0: s += bit[i]; i -= i & (-i)
  return s
RANGE-SUM(l,r) = PREFIX-SUM(r) - PREFIX-SUM(l-1)
i & (-i): lowest set bit of i

Section 2 -- The Idea, Step by Step

Picture a long shelf of jars, each holding some coins. A friend keeps asking, "How many coins are in the first 7 jars?" If you re-count from jar 1 every time, you do a lot of counting. Worse, every so often someone drops coins into one jar, so a single fixed total goes stale. You want two things to both be fast: add up the first few jars, and change one jar. A Fenwick tree (also called a Binary Indexed Tree, or BIT) gives you both.

The trick is to store clever partial sums instead of raw values. Each slot $bit[i]$ holds the sum of a small block of consecutive jars that ends at jar $i$. How long is that block? Exactly the value of the lowest set bit of $i$, written $\operatorname{LSB}(i) = i \mathbin{\&} (-i)$. For example $6 = 110$ in binary, its lowest set bit is $2$, so $bit[6] = A[5] + A[6]$. Likewise $bit[4]$ covers four jars $A[1\ldots4]$, and $bit[8]$ covers eight, $A[1\ldots8]$.

A worked prefix sum. To get $A[1\ldots i]$, start at $i$ and keep peeling off the lowest set bit until you reach $0$, adding each $bit[]$ you land on. For $i = 7 = 111$: the path is $7 \to 6 \to 4 \to 0$, so the answer is $bit[7] + bit[6] + bit[4] = A[7] + A[5\ldots6] + A[1\ldots4] = A[1\ldots7]$. Three reads instead of seven.

Both operations touch only $O(\log n)$ slots, because each step clears or adds one bit of an $n$-bit index. The two loops move in opposite directions: PREFIX-SUM walks down, $i \mathrel{-}= i \mathbin{\&} (-i)$, while UPDATE walks up, $i \mathrel{+}= i \mathbin{\&} (-i)$, touching exactly the blocks that contain position $i$. A range total is then $\text{RANGE-SUM}(l,r) = \text{PREFIX-SUM}(r) - \text{PREFIX-SUM}(l-1)$. That subtraction is the catch: BIT needs an invertible operation like $+$, which is why minimum and maximum (no inverse) need a segment tree instead. And the whole scheme is 1-indexed on purpose, since $\operatorname{LSB}(0) = 0$ would trap the loop forever.

Try this in the sim above: (1) drag the n slider higher and notice how few nodes a prefix query needs compared with $n$ itself. (2) Switch the preset between Sorted and Reverse and watch the operation and comparison counters in the stat cards. (3) Press Next repeatedly and follow the highlighted pseudocode line to see UPDATE climb with $i \mathrel{+}= i \mathbin{\&} (-i)$ and PREFIX-SUM descend with $i \mathrel{-}= i \mathbin{\&} (-i)$.

Section 3 -- Complexity Analysis

SymbolMeaning
nInput size
T(n)Time complexity function
hHeight or depth of structure
kKey or rank parameter
W(n)Worst-case complexity
A(n)Average-case complexity
Step 1 -- Lowest Set Bit. i & (-i) extracts the lowest set bit (LSB) of i. Example: i=6=110 in binary, -i=...11111010 in two's complement. i & (-i) = 010 = 2. This bit determines which range each BIT node is responsible for.
Step 2 -- BIT structure. bit[i] stores sum of A[i - (i&-i) + 1 .. i]. That is, a range of length equal to LSB(i). bit[4]=sum(A[1..4]), bit[6]=sum(A[5..6]), bit[7]=sum(A[7]).
Step 3 -- PREFIX-SUM. Sum A[1..i]: repeatedly subtract LSB(i), accumulating bit[i] values. This traverses a path of O(log n) nodes up the BIT structure. T = O(log n).
Step 4 -- UPDATE. Add delta to A[i]: repeatedly add LSB(i), updating all BIT nodes that cover position i. T = O(log n).
Step 5 -- vs Segment Tree. BIT: simpler code (3 lines each), faster constant (cache-friendly, no branching), but supports only prefix-sum queries and point updates. Segment tree: supports any operation, range updates, but more complex. For competitive programming sum queries: always use BIT.
Step 6 -- BUILD O(n). Naive: n updates O(n log n). Efficient: for each i from 1 to n: bit[i] += A[i]; j = i + (i&-i); if j<=n: bit[j] += bit[i]. This is O(n) linear build.

Section 4 -- FAQ

What does bit[i] store exactly? concept+
bit[i] stores the sum of A[i - LSB(i) + 1 .. i], a range of LSB(i) consecutive elements ending at i. PREFIX-SUM(i) decomposes [1..i] into O(log n) such non-overlapping ranges.
Why does i & (-i) give the lowest set bit? concept+
Two's complement: -i = bitwise NOT of i, plus 1. The +1 causes a carry that flips all bits up to and including LSB. Result: -i has the same LSB as i, all higher bits flipped. i & (-i) keeps only the bit that is 1 in both: the LSB.
How is BIT faster than segment tree in practice? applied+
BIT operations have no branches -- just a while loop with two instructions. Segment trees have if-else branching. BIT has better cache behavior since nodes accessed are at indices 1, 2, 4, 8, ... -- geometrically spaced. For sum queries in competitive programming, BIT is typically 2-3x faster.
Can BIT support minimum/maximum queries? nonobv+
Standard BIT only works for invertible operations (operations with an inverse, like addition). Min/max have no inverse, so prefix-min is not decomposable into BIT form. For range min/max, use segment tree.
Can BIT be extended to 2D? applied+
Yes: 2D BIT for prefix-sum queries on a 2D grid. UPDATE(x,y,delta): outer loop on x (LSB steps), inner loop on y (LSB steps). QUERY(x,y): same. Both O(log^2 n). Common in competitive programming for 2D inversion counting or 2D range sum.
What is the O(n) BUILD algorithm? deep+
For i from 1 to n: bit[i] += A[i]; then propagate: j = i + (i&-i); if j<=n: bit[j] += bit[i]. This builds BIT bottom-up, each element contributing to exactly the nodes that cover it. Avoids O(n log n) of naive n-updates approach.

Section 5 -- Common Misconceptions

Conceptual Misconceptions

X BIT is 0-indexed.

O BIT requires 1-indexed arrays because i & (-i) = 0 for i=0, causing infinite loop. Always use 1-indexed.

X BIT works for any aggregate function.

O BIT only works for invertible operations (addition, XOR). For min/max, use segment tree.

X PREFIX-SUM(i) is the same as the BIT array value bit[i].

O bit[i] stores only the range [i-LSB(i)+1..i]. PREFIX-SUM(i) combines multiple bit[] values to cover [1..i].

Implementation Errors

X Using i -= (i & -i) instead of i += (i & -i) in UPDATE.

O UPDATE goes UP (add LSB), PREFIX-SUM goes DOWN (subtract LSB). Swapping them gives wrong answers silently.

X Forgetting 1-indexed convention and using A[0].

O BIT is 1-indexed. If your array is 0-indexed, shift: bit[i+1] for A[i]. Using 0-indexed directly causes i&(-i)=0 infinite loop.

X Computing RANGE-SUM(l,r) as PREFIX-SUM(r) - PREFIX-SUM(l) instead of PREFIX-SUM(l-1).

O RANGE-SUM(l,r) = PREFIX-SUM(r) - PREFIX-SUM(l-1). Using PREFIX-SUM(l) excludes A[l] itself.