Senior 10 min · March 05, 2026

Fenwick Tree — Delta vs Absolute Update Bug Corrupts Sums

Leaderboard scores dropped 40% because batch update passed absolute values instead of deltas to Fenwick tree.

N
Naren · Founder
Plain-English first. Then code. Then the interview question.
About
 ● Production Incident 🔎 Debug Guide
Quick Answer
  • Fenwick Tree (BIT) answers prefix sums and handles point updates in O(log n) time with O(n) memory.
  • Core trick: each node stores sum of a range whose length equals the lowest set bit of its index.
  • Update propagates upward by adding the lowest set bit. Query collects by removing it.
  • Performance: ~3-5x faster than segment tree for sum queries due to cache locality.
  • Production trap: 1-indexed arrays cause off-by-one errors when interfacing with 0-indexed data sources.
  • Biggest mistake: assuming BIT supports arbitrary range updates without a difference array.
Plain-English First

Imagine a library with 16 shelves of books. Instead of counting every book from shelf 1 to shelf 10 each time someone asks, you pre-assign certain shelves to be 'summary shelves' that already know the total for a range. When a book is added, only a handful of those summary shelves update — not all 16. A Fenwick Tree does exactly that for numbers: it lets you update a value and ask 'what's the total from position 1 to position k?' in a handful of steps instead of scanning everything.

Competitive programmers love Fenwick Trees because they solve a brutally common problem — range prefix-sum queries with point updates — in O(log n) time AND O(n) space, with constants so small the code fits in about 10 lines. You'll find them inside database engines for order-book aggregations, in game servers tracking cumulative scores, and in every serious competitive programming solution that involves frequency arrays or inversions. If you've ever hit a TLE on a naive cumulative-sum approach, a Fenwick Tree is likely the fix.

The core problem is this: you have an array of numbers that changes over time, and you need to repeatedly answer 'what is the sum of elements from index 1 to index k?' A flat prefix-sum array answers queries in O(1) but updates in O(n). A Fenwick Tree gives you O(log n) for both, with a constant factor that is almost embarrassingly small — often 3-5x faster in wall-clock time than a segment tree for the same problem.

By the end of this article you'll understand exactly why the bit-manipulation trick i += i & (-i) works, how to extend the tree for range updates and point queries, how to do a binary search on the tree itself, and the exact production bugs that bite engineers the first time they ship this structure in real code.

What is a Fenwick Tree? — Plain English

A Fenwick Tree (also called Binary Indexed Tree or BIT) is a data structure that answers the question 'what is the sum of elements from index 1 to i?' in O(log n) time, while also allowing you to update any element in O(log n) time. A naive array handles point updates in O(1) but prefix sums in O(n). A prefix sum array handles queries in O(1) but updates in O(n). A Fenwick tree strikes the middle — O(log n) for both. The trick is that each node in the BIT stores the sum of a specific range of elements, and the range size is determined by the lowest set bit of the node's index. For example, index 6 (binary 110) has lowest bit 2, so it stores the sum of 2 elements: arr[5]+arr[6].

fenwick_tree.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class FenwickTree:
    def __init__(self, n):
        self.n = n
        self.bit = [0] * (n + 1)

    def update(self, i, delta):
        while i <= self.n:
            self.bit[i] += delta
            i += i & (-i)

    def query(self, i):
        total = 0
        while i > 0:
            total += self.bit[i]
            i -= i & (-i)
        return total
Production Insight
Cache misses kill performance in large BIT structures. L1 cache line is 64 bytes — a BIT's array layout means adjacent indices may not be adjacent in memory when traversed.
BFS-based BIT construction can improve locality for certain query patterns.
Rule: if you see high LLC miss rates in perf stat, consider a segment tree or a BFS-order BIT.
Key Takeaway
Fenwick Tree = O(log n) point updates + prefix sums.
The range size per node comes from the lowest set bit.
You trade O(n) build time for O(log n) operations.

How Fenwick Tree Works — Step by Step

Building and querying a BIT on arr=[1,3,5,7,9,11] (1-indexed, n=6):

Update(i, delta): add delta to BIT[i], then move to the next responsible node by adding the lowest set bit of i. 1. update(3, 5): BIT[3] += 5, then 3+(3&-3)=3+1=4 → BIT[4] += 5, then 4+4=8 > n, stop.

Query(i): sum from 1 to i by traversing upward removing the lowest set bit. 1. query(6): sum += BIT[6]; 6-(6&-6)=6-2=4 → sum += BIT[4]; 4-4=0 → stop. Sum = BIT[6]+BIT[4]. 2. BIT[6] covers arr[5]+arr[6]=9+11=20; BIT[4] covers arr[1]+arr[2]+arr[3]+arr[4]=1+3+5+7=16. Total=36.

Range query(l,r) = query(r) - query(l-1).

Production Insight
The 1-indexed convention is the single biggest source of bugs in production BIT code.
When your data source is 0-indexed (most programming languages), every update and query call must add 1 to the index.
Rule: wrap all public methods to accept 0-indexed input and internally convert to 1-indexed.
Key Takeaway
Update: add LSB to index.
Query: subtract LSB from index.
Both run in O(log n) steps because the number of bits in n is at most log n.

Fenwick Tree Node Responsibilities — Which Indices Each Node Covers

Each node in a Fenwick Tree is responsible for a contiguous range of the original array. The range length is equal to the lowest set bit (LSB) of the node's index. The range starts at i - (i & -i) + 1 and ends at i. Here is a visual mapping for the first 8 indices:

  • Index 1 (binary 1): LSB=1 → covers arr[1] (range length 1)
  • Index 2 (binary 10): LSB=2 → covers arr[1..2] (length 2)
  • Index 3 (binary 11): LSB=1 → covers arr[3] (length 1)
  • Index 4 (binary 100): LSB=4 → covers arr[1..4] (length 4)
  • Index 5 (binary 101): LSB=1 → covers arr[5] (length 1)
  • Index 6 (binary 110): LSB=2 → covers arr[5..6] (length 2)
  • Index 7 (binary 111): LSB=1 → covers arr[7] (length 1)
  • Index 8 (binary 1000): LSB=8 → covers arr[1..8] (length 8)

The tree structure is implicit: the parent of index i is i + (i & -i). The child for querying (in the reverse direction) is i - (i & -i).

Production Insight
When implementing BIT in a memory-constrained environment (e.g., embedded systems), you can precompute the LSB for each index using a lookup table of size n+1 to avoid i & -i overhead. The LSB table is built once: lsb[i] = i & -i. This trades a tiny amount of memory for faster loops.
Key Takeaway
Node i covers range length = LSB(i). Parent = i + LSB(i). Query child = i - LSB(i). The BIT is an implicit tree encoded in the array indices.

Worked Example — Tracing BIT Construction

Build BIT for arr=[1,3,5,7,9,11] (1-indexed).

Initialize BIT=[0,0,0,0,0,0,0] (index 0 unused).

update(1,1): BIT[1]+=1 → BIT[1]=1. 1+(1&-1)=1+1=2 → BIT[2]+=1 → BIT[2]=1. 2+2=4 → BIT[4]+=1=1. 4+4=8>6, stop. update(2,3): BIT[2]+=3=4. 2+2=4 → BIT[4]+=3=4. 8>6 stop. update(3,5): BIT[3]+=5=5. 3+1=4 → BIT[4]+=5=9. 8>6 stop. update(4,7): BIT[4]+=7=16. 4+4=8>6 stop. update(5,9): BIT[5]+=9=9. 5+1=6 → BIT[6]+=9=9. 6+2=8>6 stop. update(6,11): BIT[6]+=11=20. 6+2=8>6 stop.

Final BIT=[0,1,4,5,16,9,20]. query(6): BIT[6]=20; 6-2=4; BIT[4]=16; 4-4=0 stop. Sum=36. Correct (1+3+5+7+9+11=36).

Production Insight
For large arrays (10^6+), building via O(n log n) updates is measurable. Use the O(n) construction approach: start with a prefix sum of the original array, then set BIT[i] = sum of range (i - (i&-i) + 1, i) and skip the loop.
Rule: if your build is the bottleneck in a hot path, switch to O(n) construction.
Key Takeaway
Manual tracing shows exactly which nodes each update touches.
Pattern: update touches indices that are powers-of-two-related.
The final BIT array has the property that query(6) correctly yields 36.

C++ Implementation of Fenwick Tree

In C++, the Fenwick Tree implementation follows the same logic as Python but requires manual memory management or use of std::vector. The 1-indexed convention is critical. Below is a complete template class that supports point update and prefix sum query. Note that the array size is n+1 (index 0 unused). The bit manipulation i & -i works identically in C++ as in Python because integers are two's complement on all modern platforms.

fenwick_tree.cppCPP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <vector>

class FenwickTree {
public:
    FenwickTree(int n) : bit(n + 1, 0) {}

    void update(int i, int delta) {
        while (i < (int)bit.size()) {
            bit[i] += delta;
            i += i & -i;
        }
    }

    int query(int i) {
        int sum = 0;
        while (i > 0) {
            sum += bit[i];
            i -= i & -i;
        }
        return sum;
    }

private:
    std::vector<int> bit;
};

// Example usage
// FenwickTree ft(10);
// ft.update(3, 5);
// printf("%d\n", ft.query(3)); // 5
Output
5
Production Insight
In production C++ code, prefer std::vector<int64_t> for BIT when sums might overflow 32-bit. Use size_t for indices to avoid signed/unsigned mismatches. Wrap the BIT inside a class with methods accepting int for portability, and validate that i is in range [1, n] in debug builds.
Key Takeaway
C++ BIT mirrors Python BIT line for line. Use std::vector, size n+1, and i & -i. The code is ~10 lines and runs extremely fast with -O2.

Advantages and Disadvantages of Fenwick Tree

Fenwick Trees offer a sweet spot between simplicity and performance for prefix-sum problems with point updates. However, they are not a universal tool. Understanding their strengths and weaknesses helps you decide when to use them vs. a segment tree or other structures.

AdvantagesDisadvantages
Very simple to implement (~10 lines)Only supports sum-like (invertible) operations; cannot easily do range max/min
O(log n) for both update and queryRequires 1-indexed array, which can cause off-by-one errors
O(n) memory — uses exactly n+1 integersNot suitable for non-associative or non-invertible operations
Cache-friendly due to contiguous arrayRange update + range query requires two BITs (more complex)
Fast in practice — often 3-5x faster than segment treeCannot handle arbitrary range queries without pre-aggregation
Can be extended to 2D and higher dimensionsBuilding via point updates is O(n log n); O(n) build is less intuitive
Supports binary search on prefix sums in O(log n)Requires non-negative values for binary search
Production Insight
In production systems where latency is critical (e.g., real-time order book aggregation), BIT is often preferred over segment tree because of its smaller memory footprint and better cache locality. However, if you need to support range updates with range queries frequently, consider a segment tree with lazy propagation to avoid juggling two BITs.
Key Takeaway
Fenwick Tree excels at prefix sums with point updates. Choose it when code simplicity, speed, and memory efficiency matter. Avoid it for non-sum operations.

Fenwick Tree for Range Updates and Point Queries — The Difference BIT

Sometimes you need to add a value to every element in a range [l, r], and then later query the current value at a single point. A standard BIT doesn't support range updates directly, but with a 'difference BIT' you can do it in O(log n).

Create a BIT that stores differences. To add delta to all elements in [l, r], perform two point updates: - update(l, +delta) - update(r+1, -delta)

Then the point query at index i is simply query(i) — the prefix sum of the difference array gives the current value at i. This works because a prefix sum of differences reconstructs the original value.

Example: arr initially [0,0,0,0,0]. Add 5 to indices [2,3]: update(2,5), update(4,-5). Query(3) = BIT[3] =? Let's trace: BIT after updates... actually the prefix sum query(3) = sum of diffs up to index 3 = BIT[3] + BIT[2]? But careful with the BIT structure. The standard difference BIT still works: the BIT stores the difference array values, and point query is just prefix sum of that difference array. So query(i) gives diff[1] + diff[2] + ... + diff[i], which equals the cumulative change up to i, which gives the original value if the original array was zero. For non-zero initial values, you add initial arr[i] to query(i).

range_update_bit.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class RangeUpdateBIT:
    def __init__(self, n):
        self.bit = FenwickTree(n)

    def range_add(self, l, r, delta):
        # Add delta to all elements in [l, r]
        self.bit.update(l, delta)
        self.bit.update(r+1, -delta)

    def get_point(self, i):
        # Get current value at position i
        return self.bit.query(i)

# Example
bit = RangeUpdateBIT(10)
bit.range_add(2, 5, 10)  # Add 10 to indices 2..5
print(bit.get_point(3))   # 10
print(bit.get_point(1))   # 0
print(bit.get_point(6))   # 0
Production Insight
Difference BIT is often used in billing systems: apply a credit delta to a range of accounts, then query individual balances. The double update is atomic if you're using locks or transactions.
Rule: always flush the diff updates in a single transaction — if the process crashes after update(l) but before update(r+1), subsequent queries will be permanently off. Use idempotent retry or a write-ahead log.
Key Takeaway
Range update + point query = two point updates on a difference BIT.
Point query = prefix sum of differences.
Atomicity matters: both updates must succeed or neither.

2D Fenwick Tree — Sum Over Subrectangle

A 2D Fenwick Tree extends the concept to a matrix. It supports point update at (x, y) and prefix sum query from (1,1) to (x,y) in O(log n log m) time and O(n m) memory. The key is to nest BIT logic: each row contains a BIT, and rows themselves are organized in a BIT-like structure. Updates propagate both horizontally and vertically.

Implementation: maintain a 2D BIT array bit[x][y] where each row x has a BIT that stores column contributions. To update (x, y) with delta, iterate rows using while (x <= n) { update_row(x, y, delta); x += x & -x; }. In update_row, iterate columns using while (y <= m) { bit[x][y] += delta; y += y & -y; }. Prefix sum query does the same in reverse.

Example problem: Given a grid of numbers, answer queries that ask for sum of a subrectangle, with point updates. This is a classic problem in competitive programming and can be solved with a 2D BIT when the grid size is moderate (e.g., 1000x1000). For larger grids, consider a prefix sum approach if no updates, or a segment tree of segment trees.

Coordinate compression: If the grid dimensions are large but sparse (few updates), compress coordinates. Create a list of all x and y that appear in updates and queries, sort and deduplicate, then map coordinates to compressed indices for the BIT.

Production insight: In geospatial applications (e.g., aggregating sensor readings over a region), a 2D BIT can provide real-time rectangle sum queries. However, the memory cost is O(n*m), which can be prohibitive for large grids. Consider using a sparse matrix representation or KD-tree for massive datasets.

fenwick_2d.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Fenwick2D:
    def __init__(self, n, m):
        self.n = n
        self.m = m
        self.bit = [[0]*(m+1) for _ in range(n+1)]

    def update(self, x, y, delta):
        i = x
        while i <= self.n:
            j = y
            while j <= self.m:
                self.bit[i][j] += delta
                j += j & -j
            i += i & -i

    def query(self, x, y):
        total = 0
        i = x
        while i > 0:
            j = y
            while j > 0:
                total += self.bit[i][j]
                j -= j & -j
            i -= i & -i
        return total

    def range_sum(self, x1, y1, x2, y2):
        # inclusive rectangle sum
        return (self.query(x2, y2) - self.query(x1-1, y2)
                - self.query(x2, y1-1) + self.query(x1-1, y1-1))

# Example
ft2d = Fenwick2D(5, 5)
ft2d.update(2, 3, 7)
ft2d.update(4, 2, 3)
print(ft2d.range_sum(1, 1, 5, 5))  # 10
Output
10
Production Insight
The 2D BIT is memory-heavy. For a 10^6 x 10^6 grid, it's impossible. In practice, use it only when both dimensions are small (≤ 10^4) or after coordinate compression. For big data, use a segment tree of segment trees or a wavelet tree.
Key Takeaway
2D BIT = nested BIT loops: O(log n * log m) per operation. Useful for offline queries with compressed coordinates.

Binary Search on Fenwick Tree — Finding Prefix Index by Sum

Sometimes you need to find the smallest index i such that prefix_sum(i) >= K. A naive approach does O(log^2 n) by binary searching over index and calling query each time. But because BIT supports logarithmic query, you can do better — O(log n) using the tree itself, leveraging the structure of indices.

You start at 0 and consider the highest power of two <= n. If adding that index to your current position keeps the sum below K, you add it and move to that index. Then divide the step by 2 and repeat. This is analogous to binary lifting.

Implementation: maintain pos = 0 and step = highestPowerOfTwo(n). While step > 0: - test = pos + step - if test <= n and bit[test] + current_sum < K: pos = test; current_sum += bit[test] - step >>= 1 At the end, answer = pos+1 (the next index).

This technique is used in order statistics trees (finding k-th smallest element) and in compressing frequency arrays for rank queries.

Production Insight
The binary search on BIT assumes cumulative sums are non-decreasing (non-negative values). If your data contains negative values, prefix sums may decrease and the method fails.
Rule: only use BIT binary search for non-negative values, or after converting to a non-negative domain via shifting.
Key Takeaway
BIT binary search finds the prefix index for a given sum in O(log n).
Useful for percentile queries and order statistics.
Only works correctly with non-negative cumulative sums.

Application: Counting Inversions with Fenwick Tree

An inversion in an array is a pair (i, j) such that i < j and arr[i] > arr[j]. Counting inversions is a classic problem that can be solved with a Fenwick Tree in O(n log n) time. The idea: traverse the array from left to right, and for each element, query how many elements greater than it have already been seen. The BIT stores frequency of values. However, values can be large, so we first compress them to ranks (1..n). Then, for each element x (after compression), we add 1 to its rank in BIT, and query the sum of ranks greater than x (i.e., n - query(x)).

Worked example: arr = [3, 1, 2] 1. Compress to ranks: sorted unique = [1,2,3] -> map: 3->3, 1->1, 2->2. Compressed array: [3,1,2]. 2. Initialize BIT of size 3 with zeros. 3. Process each element: - i=0, x=3: query(3) = 0 (prefix sum up to 3). Inversions added = total processed (0) - query(3) = 0. Update BIT[3] += 1. - i=1, x=1: query(1) = 0. Inversions = 1 - 0 = 1 (because element 3 > 1). Update BIT[1] += 1. - i=2, x=2: query(2) = BIT[1]+BIT[2] = 1+0=1. Inversions = 2 - 1 = 1 (since element 3 > 2). Update BIT[2] += 1. 4. Total inversions = 0+1+1 = 2.

Manually verify: pairs (3,1), (3,2) -> 2 inversions. Correct.

The code compresses values to ranks and uses the standard BIT. This approach is widely used in competitive programming.

inversion_count.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def count_inversions(arr):
    # coordinate compression
    sorted_unique = sorted(set(arr))
    rank = {v: i+1 for i, v in enumerate(sorted_unique)}  # 1-indexed
    n = len(sorted_unique)
    bit = FenwickTree(n)
    inversions = 0
    for i, val in enumerate(arr):
        r = rank[val]
        bit.update(r, 1)
        # number of elements greater than current = i+1 - query(r)
        inversions += (i+1) - bit.query(r)
    return inversions

arr = [3, 1, 2]
print(count_inversions(arr))  # 2
Output
2
Production Insight
In high-frequency trading systems, inversion count (or cross-rate between two instruments) can be maintained in real time using a Fenwick Tree. Each new trade updates the frequency rank, and the inversion count is queried to detect price anomalies. The O(log n) per trade is acceptable even for millions of trades per day.
Key Takeaway
Inversion count using BIT: compress values, then for each element, count how many previous elements are larger using prefix sum. Runs in O(n log n).

Practice Problems for Fenwick Tree

Solidify your understanding of Fenwick Tree by solving these problems, ordered from easy to hard. They cover point updates, range queries, difference BIT, binary search, 2D BIT, and inversion count.

  1. LeetCode 307 - Range Sum Query - Mutable (Medium): Classic problem to implement BIT for point update and range sum query.
  2. - [https://leetcode.com/problems/range-sum-query-mutable/](https://leetcode.com/problems/range-sum-query-mutable/)
  3. CSES Problem Set - Range Sum Queries II (Easy): Simple BIT for point update and prefix sum queries.
  4. - [https://cses.fi/problemset/task/1648/](https://cses.fi/problemset/task/1648/)
  5. CSES Problem Set - Inversions (Easy): Count inversions using BIT.
  6. - [https://cses.fi/problemset/task/1748/](https://cses.fi/problemset/task/1748/)
  7. CSES Problem Set - Forest Queries II (Medium): 2D BIT on a grid with point updates.
  8. - [https://cses.fi/problemset/task/1652/](https://cses.fi/problemset/task/1652/)
  9. Codeforces 1354D - Multiset (Medium): Order statistics using BIT binary search to find k-th smallest element.
  10. - [https://codeforces.com/problemset/problem/1354/D](https://codeforces.com/problemset/problem/1354/D)
  11. Codeforces 1191C - Tokitsukaze and Discarding Cards (Medium): Difference BIT for range updates.
  12. - [https://codeforces.com/problemset/problem/1191/C](https://codeforces.com/problemset/problem/1191/C)
  13. SPOJ - KQUERY (Hard): Offline queries using BIT with coordinate compression.
  14. - [https://www.spoj.com/problems/KQUERY/](https://www.spoj.com/problems/KQUERY/)

These problems will help you master both basic and advanced BIT techniques.

Production Insight
In real-world projects, practice problems often mirror production scenarios: e.g., LeetCode 307 is essentially the same structure as a real-time scoreboard with point updates. Mastering these problems ensures you can debug and extend BIT code under pressure.
Key Takeaway
Solve at least 5 problems from this list to gain confidence with BIT. Focus on the inversion count and order statistics problems as they combine BIT with other techniques.
● Production incidentPOST-MORTEMseverity: high

Leaderboard Scores Suddenly Drop by 40% After Batch Player Update

Symptom
Players complained that their total score displayed as roughly 60% of expected. Debug logs showed the BIT's prefix sum returned values that were consistently lower than the sum of individual player entries in the database.
Assumption
The team assumed the BIT was correctly built from the raw score table and that a point update would always propagate correctly.
Root cause
The batch migration job updated player scores using update(i, delta) where delta was the new score, not the change. For example, if a player's old score was 500 and new score was 1000, the code called update(i, 1000) instead of update(i, 500). The BIT stored absolute values in some nodes, leading to double-counting only partially through the tree. The bug silently corrupted the tree structure — no crash, just wrong sums.
Fix
Changed the batch migration to compute delta = new - old and call update(i, delta). Added a validation script that recomputed the full prefix sum from raw data and compared it to BIT.queries for random indices.
Key lesson
  • Always validate BIT integrity after bulk updates by cross-checking against a naive prefix sum for a sample of indices.
  • Fenwick Tree's update is designed for incremental changes, not absolute overwrites. Use a diff-based update every time.
  • Implement a build function that reconstructs the BIT from scratch for batch operations instead of reusing point updates.
Production debug guideSpot and fix the three most common runtime failures in BIT code3 entries
Symptom · 01
Prefix sum query returns 0 or wrong small number for all i > 1
Fix
Check if you accidentally used 0-indexed array inside update/query loops. BIT must be 1-indexed. Wrap the underlying data access: always call update(i+1, delta) and query(i+1) when dealing with 0-indexed sources.
Symptom · 02
Point update doesn't appear to affect any prefix sums after a specific index
Fix
Verify the tree size: n in the BIT constructor should be at least the max index you plan to use. If n is smaller, updates beyond the allocated size silently fail (no loop iterations).
Symptom · 03
Range query l..r returns negative values for equal indices (l==r)
Fix
Check the range_query implementation: it must be query(r) - query(l-1). If you used query(r) - query(l) you get the sum from l+1 to r only. For l==r, this gives 0 instead of the correct single element.
★ Fenwick Tree Quick Debug Cheat SheetUse these commands/traces when your BIT isn't working as expected. They assume a Python-like implementation with a BIT array accessed via `self.bit`.
Query result is zero for all indices
Immediate action
Print the entire BIT array after building. If all entries are 0, your build loop never executed.
Commands
print('BIT:', self.bit[1:])
for i in range(1, n+1): self.update(i, arr[i-1]) # verify the loop runs n times
Fix now
Ensure n >= len(arr) and that arr is 0-indexed. Use self.update(i, arr[i-1]) in a loop for building.
Range query (l,r) returns same as query(r) alone+
Immediate action
Check that range_query calls query(r) - query(l-1). Print both intermediate values.
Commands
print('query_r:', self.query(r), 'query_l-1:', self.query(l-1))
print('expected sum of subarray:', sum(arr[l-1:r]))
Fix now
If query_l-1 is 0, then l-1 might be 0 and your query loop while i>0 exits immediately. That's correct; the subtraction should work.
After update, prefix sum for a higher index is slightly off+
Immediate action
Manually trace the update propagation. Compute the binary representation of the updated index and check that the BIT indices visited are correct.
Commands
print('update path:', [i := i + (i & -i) for _ in range(20) if i <= n]) # Python 3.8+ walrus
expected_affected_indices = [i, i + (i&-i), ...] until > n
Fix now
If the update falls off the end before reaching the root, increase n or ensure n covers the logical size. Also check that i & -i is computed correctly (parentheses: i & -i, not i & -i).
Fenwick Tree vs Related Techniques
Data StructurePoint UpdatePrefix QueryRange UpdateRange QueryMemoryCode Complexity
Naive ArrayO(1)O(n)O(n)O(n)O(n)1 line
Prefix Sum ArrayO(n)O(1)O(n)O(1)O(n)2 lines
Fenwick Tree (BIT)O(log n)O(log n)O(log n) with diff BITO(log n) (with two BITs)O(n)10 lines
Segment TreeO(log n)O(log n)O(log n) (lazy)O(log n)O(4n)30-50 lines
Difference ArrayO(1)O(n) to query pointO(1) per rangeO(n) to query rangeO(n)2 lines

Key takeaways

1
Fenwick Tree answers prefix sum queries in O(log n) using the lowest set bit of index for range boundaries.
2
The i & -i trick isolates the lowest set bit, enabling O(log n) navigation up or down the implicit tree.
3
BITs are simpler and faster than segment trees for sum queries but cannot easily support non-invertible operations like range max.
4
Off-by-one errors from 1-indexing are the number one production bug
always wrap public APIs to accept 0-indexed input.

Common mistakes to avoid

3 patterns
×

Memorising syntax before understanding the concept

Symptom
You can write update/query but can't explain why i & -i works. When faced with a variant (range update), you get stuck.
Fix
Trace a small example by hand. Understand that BIT nodes store sums of intervals determined by the lowest set bit. Once the conceptual model is solid, the code writes itself.
×

Skipping practice and only reading theory

Symptom
In an interview or production debugging, you fail to spot off-by-one errors or underestimate the impact of 1-indexing on surrounding code.
Fix
Implement BIT from scratch at least three times in different languages. Solve at least five problems on LeetCode (e.g., Range Sum Query - Mutable, Count of Smaller Numbers After Self).
×

Using point update for absolute overwrites instead of diff updates

Symptom
After batch updates from a database, BIT prefix sums are inconsistent with raw data. The tree becomes corrupted because update(i, newValue) was called instead of update(i, delta).
Fix
Always compute delta = newValue - oldValue. If you don't have oldValue, rebuild the BIT from scratch using a linear-time build, or use a BIT designed for absolute values (e.g., store base array separately and sum base + BIT update).
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
What is the time complexity of BIT update and query?
Q02SENIOR
How does i & -i isolate the lowest set bit?
Q03SENIOR
When would you choose BIT over segment tree?
Q04SENIOR
Can Fenwick Tree handle range updates and range queries?
Q05SENIOR
How do you find the smallest index with prefix sum >= K using BIT?
Q01 of 05JUNIOR

What is the time complexity of BIT update and query?

ANSWER
Both are O(log n) where n is the size of the array. The update loop runs at most log2(n) times because each iteration adds the lowest set bit, doubling the index at least every other step. The query loop similarly removes the lowest set bit, also running at most log2(n) times.
FAQ · 5 QUESTIONS

Frequently Asked Questions

01
What does 'i & -i' compute and why is it useful?
02
When should I use a Fenwick tree vs a segment tree?
03
Can a Fenwick tree handle range updates?
04
What is the space complexity of a Fenwick tree?
05
Can a BIT be built in O(n) instead of O(n log n)?
🔥

That's Trees. Mark it forged?

10 min read · try the examples if you haven't

Previous
Segment Tree
9 / 15 · Trees
Next
Trie Data Structure