Translation Notice
This article was machine-translated using DeepSeek-R1.
- Original Version: Authored in Chinese by myself
- Accuracy Advisory: Potential discrepancies may exist between translations
- Precedence: The Chinese text shall prevail in case of ambiguity
- Feedback: Technical suggestions regarding translation quality are welcomed
Introduction
The Fenwick Tree, also known as Binary Indexed Tree (BIT), is a tree-like data structure. Despite its name, its applications often seem unrelated to trees:
Given a sequence $A=(A_1,A_2,\dots,A_N)$, perform the following operations in $\mathcal O(\log N)$ time:
$\to~$Calculate the sum of all elements in the interval $[L,R]$.
$\to~$Update a specific element $A_x$ by adding $k$.
To optimize summation, prefix sums come to mind, offering $\mathcal O(1)$ query time but $\mathcal O(N)$ update time. Conversely, naively storing all elements allows $\mathcal O(1)$ updates but $\mathcal O(N)$ queries. The solution? The Fenwick Tree.
Basic Algorithm
Luogu P3374 “Fenwick Tree 1 Template”
Constraints: $1\le n,m\le 10^5$, where $m$ is the total number of operations.
With $n,m\le 10^5$, an $\mathcal O(nm)$ brute-force approach is infeasible. The Fenwick Tree achieves $\mathcal O(M\log N)$ time. Its structure is illustrated below:
Let $B$ represent the internal storage. From the diagram:
- $B_1=A_1$
- $B_2=A_1+A_2$
- $B_3=A_3$
- $B_4=A_1+A_2+A_3+A_4$
- …
Here, $B_i$ stores the sum $A_{i-2^k+1}+A_{i-2^k+2}+\dots+A_i$, where $k$ is the number of trailing zeros in $i$’s binary representation. This $2^k$ is the lowbit
of $i$.
$$ l:=i-\mathrm{lowbit}(i)+1\\ r:=i\\ B_i=\sum_{j=l}^{r} A_j $$About the
lowbit
Function
$\to~$Definition:
- $\mathrm{lowbit}(0)$ is undefined.
- For $x > 0$, $\mathrm{lowbit}(x)=2^k$, where $k$ is the number of trailing zeros in $x$’s binary representation.
$\to~$Examples: $\mathrm{lowbit}(10010_2)=10_2$; $\mathrm{lowbit}(10000_2)=10000_2$
$\to~$C/C++ Implementation:
1 2 3
inline int lowbit(int x) { return x & -x; }
Or as a macro:
1
#define lowbit(x) (x) & -(x)
This allows $\mathcal O(\log n)$ prefix sums (prefixSum(x)
) and updates (update(x, k)
). Interval sums $[L,R]$ are computed as segmentSum(l, r) = prefixSum(r) - prefixSum(l - 1)
. Code:
|
|
On Luogu P3374, this code runs in $572\mathrm{ms}$, compared to ~$2.5\mathrm{s}$ for a segment tree implementation, highlighting the Fenwick Tree’s efficiency.
Extended Applications
Knowledge Prerequisite: Discretization
Given a sequence $A$, discretization maps elements to $[1,N]$ while preserving order. For $A=(A_1,\dots,A_N)$, discretized sequence $B=(B_1,\dots,B_N)$ satisfies:
- $1\le B_i\le N$
- $A_i < A_j \Rightarrow B_i < B_j$, $A_i = A_j \Rightarrow B_i = B_j$, $A_i > A_j \Rightarrow B_i > B_j$.
Counting Inversions
Inversion pairs can be counted using a Fenwick Tree after discretization.
Luogu P1908 Inversion Pairs
Given $A=(A_1,\dots,A_N)$, count pairs $(i,j)$ where $iA_j$. Constraints: $N\le 10^5, A_i\le 10^9$.
After discretizing $A$ to $B$, use a Fenwick Tree to track occurrences. Traverse $B$ while maintaining a running count of elements ≤ current $B_i$, subtracting from the total possible pairs $\frac{N(N-1)}{2}$.
|
|
Exercise: CF1676H2 Maximum Crossings (Hard Version)
Range Updates
While Fenwick Trees don’t directly support range updates, difference arrays can help:
Luogu P3368 “Fenwick Tree 2 Template”
Operations:
1 x y k
: Add $k$ to all elements in $[x,y]$.2 x
: Query the value at $A_x$.
Maintain a difference array $B$ where $B_i = A_i - A_{i-1}$. Range updates become:
- $B_x \mathrel{+}=k$
- $B_{y+1} \mathrel{-}=k$
Querying $A_x$ is equivalent to summing $B_1$ to $B_x$ via prefixSum(x)
.
|
|
Exercises
- Luogu P1168 Median (Finding the $k$-th largest)
- Luogu P1637 Triples
- Luogu P6477 Subsequence Issues
- Luogu P8253 Correct Sorting
- ABC256 F - Cumulative Cumulative Cumulative Sum
- Luogu P3372 Segment Tree Template (Try a “Super Fenwick Tree” approach?)
Conclusion
The Fenwick Tree efficiently handles point updates and prefix sums. Questions and suggestions are welcome!
Please like and share!