Core Logic and Mathematical Principles
The core logic of the Binary Indexed Tree (BIT, also known as Fenwick Tree) lies in utilizing the binary decomposition of positive integers to dynamically maintain prefix sums. It can achieve "point updates" and "range queries" in $O(\log N)$ time complexity, fundamentally breaking the constant impasse of ordinary arrays where updates take $O(1)$ and queries take $O(N)$, or prefix sum arrays where updates take $O(N)$ and queries take $O(1)$.
1. Mathematical Mechanism of $\text{lowbit}$
Define the function $\text{lowbit}(x)$ as the value represented by the "lowest $1$ bit and the following $0$s" in the binary representation of a non-negative integer $x$. By utilizing the two's complement mechanism at the computer's lower level, performing a bitwise AND operation between the source of the positive number $x$ and the complement of its opposite number $-x$ can instantly extract this characteristic bit. The mathematical identity is:
$$\text{lowbit}(x) = x \ \& \ (-x)$$
Proof: Let the lowest $1$ bit of $x$ be at position $k$. Then $x$ can be expressed as $\dots 10\dots0$ (with $k$ trailing $0$s). The two's complement of $-x$ is obtained by inverting $x$ and adding $1$. After inversion, it becomes $\dots 01\dots1$, and adding $1$ changes the $k$-th bit and its lower bits due to carry to $10\dots0$, while the higher bits are completely opposite to $x$. After performing the & operation, all higher bits are cleared, leaving only the $k$-th bit as $1$.
2. Tree Structure Topology
The storage array tree[x] of the Binary Indexed Tree does not record single point values, but is responsible for maintaining the sum of a continuous interval of length $\text{lowbit}(x)$ in the original array A[]. The boundaries of its closed interval are:
$$\text{tree}[x] = \sum_{i = x - \text{lowbit}(x) + 1}^{x} A[i]$$
Based on this topological geometric relationship, two essential topological chains emerge:
- Direct Parent Chain (for point updates): The index of the parent node of node $x$ is $x + \text{lowbit}(x)$.
- Prefix Split Chain (for range queries): When calculating the prefix sum $S_x$, the index of the next independent interval block to be added is $x - \text{lowbit}(x)$.
State Design and Algorithm Derivation
The state of the Binary Indexed Tree requires only a static space tree[] of the same size as the original array, with valid indices starting from $1$.
1. Point Update (Add) Algorithm Derivation
When the original array element $A[x]$ increases by $\Delta$, all ancestor nodes of tree that govern $A[x]$ must synchronously add $\Delta$ based on the tree structure.
- State Transition Path: Starting from $x$, jump upwards by updating $x \leftarrow x + \text{lowbit}(x)$ until exceeding the global boundary $N$.
- Complexity Derivation: Since $x$ increases by $\text{lowbit}(x)$ each time, the trailing $1$ in its binary representation must continuously move upward. Essentially, this is climbing along a single chain in a complete binary tree, with the number of iterations strictly constrained within $\lfloor \log_2 N \rfloor$, resulting in a complexity of $O(\log N)$.
2. Prefix Query (Query) Algorithm Derivation
To solve the prefix sum $S_x = \sum_{i=1}^x A[i]$, utilizing binary decomposition allows the large interval $[1, x]$ to be discretely split into several independent sub-intervals maintained directly by the tree array.
- State Transition Path: Accumulate the current
tree[x], then update $x \leftarrow x - \text{lowbit}(x)$ to strip off the current lowest $1$ bit, entering the previous non-overlapping independent maintained interval until $x = 0$. - Range Query Promotion: Using the principle of inclusion-exclusion for prefix sums, the sum of any closed interval $[L, R]$ can be directly transformed into the difference of two prefix sums:
$$\text{Sum}(L, R) = \text{Query}(R) - \text{Query}(L - 1)$$
- Complexity Derivation: Since $x$ decreases by $\text{lowbit}(x)$ each time, it is equivalent to clearing each $1$ in its binary representation from low to high. The number of iterations equals the number of $1$s in the binary representation of $x$ (Popcount), with the worst-case complexity being $O(\log N)$.
C++ Standard Source Code
#include <iostream>
#include <vector>
#include <algorithm>
const int MAXN = 500005;
struct BinaryIndexedTree {
int tree[MAXN];
int n;
// Explicit initialization
void init(int _n) {
this->n = _n;
// Key specification: In multiple data tests, be sure to explicitly clear the entire valid space
std::fill(tree, tree + n + 1, 0);
}
// Inline lowbit operation to suppress function call overhead
inline int lowbit(int x) const {
return x & (-x);
}
// Point update: add val at position x
void add(int x, int val) {
// Critical pitfall: The index of the Binary Indexed Tree strictly starts from 1; passing x = 0 will cause an infinite loop at x + lowbit(0) = 0
for (; x <= n; x += lowbit(x)) {
tree[x] += val;
}
}
// Query prefix sum: calculate the sum of the interval [1, x]
int query(int x) const {
int sum = 0;
// Exit condition is x > 0; when x = 0, the increment or decrement chain terminates
for (; x > 0; x -= lowbit(x)) {
sum += tree[x];
}
return sum;
}
// Range query: calculate the standard sum of the interval [l, r]
int query_range(int l, int r) const {
if (l > r) return 0;
// Critical pitfall: When using inclusion-exclusion to calculate the interval, the left endpoint must be l - 1, and note that when l=1, query(0) correctly returns 0
return query(r) - query(l - 1);
}
};
int main() {
// Force unbind I/O to maximize read/write performance
std::ios_base::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, m;
if (std::cin >> n >> m) {
BinaryIndexedTree bit;
bit.init(n);
for (int i = 1; i <= n; ++i) {
int val;
std::cin >> val;
bit.add(i, val); // Initial tree construction, single $O(\log N)$, total construction $O(N \log N)$
}
for (int i = 0; i < m; ++i) {
int type, x, y;
std::cin >> type >> x >> y;
if (type == 1) {
bit.add(x, y); // Point update
} else if (type == 2) {
std::cout << bit.query_range(x, y) << "\n"; // Range query
}
}
}
return 0;
}
NOIP Practical Pitfall Guide
- Passing index 0 leads to infinite loop and stack overflow
The entire arithmetic logic of the Binary Indexed Tree is based on the set of positive integers with index $\ge 1$. If you mistakenly trigger
add(0, val)orquery(0)in the code: since $\text{lowbit}(0) = 0 \ \& \ (-0) = 0$, executingx += lowbit(x)is equivalent to0 += 0. The state of theforloop will never undergo a substantive transition, causing the program to hang on the judging machine and triggerTime Limit Exceeded. Iron law: Any coordinate discretization or counting model must shift right by one to ensure that the input endpoints of the Binary Indexed Tree are $\ge 1$. - Data not converted to
long longleads to overflow in range sum signs In problems like "dynamic frequency statistics of intervals" or large value range sum problems, although the single point values in the original array are within theintrange, after tens of thousands ofaddoperations, the actual value of the prefix sumtree[x]can easily exceed $2^{31}-1$. If you insist on usingint tree[], arithmetic overflow will occur when executingquery(r) - query(l-1), leading to negative values and bizarre erroneous outputs. Warning: As long as there are frequent additions or range sum requirements, thetreearray and the return value of thequeryfunction must be locked tolong long.
Classic NOIP/Luogu Problems
1. Luogu P3374 [Template] Binary Indexed Tree 1
- Problem Description: Given a sequence of numbers, you need to perform two operations: one is to add $x$ to a certain number, and the other is to calculate the sum of all numbers within a certain range.
- Essence of the Problem: The most standard dynamic point update and range sum query.
- Core Solving Idea:
This problem is a direct mapping of the underlying Binary Indexed Tree. Using a static
treearray, traverse and executeadd(i, a[i])when reading in the initial sequence to complete the basic topology filling. Subsequently, directly calladd(x, k)for update operations and outputquery_range(x, y)for inquiry operations, perfectly passing all evaluation points with standard $O(\log N)$ arithmetic transitions.
2. Luogu P3368 [Template] Binary Indexed Tree 2
- Problem Description: Given a sequence of numbers, you need to perform two operations: one is to add $x$ to each number in a certain range, and the other is to find the value of a certain number.
- Essence of the Problem: The dual transformation of range updates and point queries.
- Core Solving Idea: Introduce the core mathematical tool—Difference. Let the difference array be $D[i] = A[i] - A[i-1]$. At this point, performing the range update operation of adding $v$ to the entire interval $[L, R]$ in the original array $A$ is equivalent to two point changes in the difference array: $D[L] \leftarrow D[L] + v$ and $D[R+1] \leftarrow D[R+1] - v$. The operation to find the single point value $A[x]$ in the original array can be expressed as $A[x] = \sum_{i=1}^x D[i]$, which is equivalent to finding the prefix sum of the difference array $D$. Therefore, directly maintaining the difference array $D$ with the Binary Indexed Tree can perfectly reverse "range update, point query" to the standard Binary Indexed Tree model of "point update, prefix query", with a complexity still at the hardcore level of $O(\log N)$.