| tags |
|
|
|---|---|---|
| e_maxx_link | fenwick_tree |
Let
The Fenwick tree is a data structure which:
- calculates the value of function
$f$ in the given range$[l, r]$ (i.e.$A_l * A_{l+1} * \dots * A_r$ ) in$O(\log N)$ time - updates the value of an element of
$A$ in$O(\log N)$ time - requires
$O(N)$ memory (the same amount required for$A$ ) - is easy to use and code, especially in the case of multidimensional arrays
The most common application of a Fenwick tree is calculating the sum of a range.
For example, using addition over the set of integers as the group operation, i.e.
The Fenwick tree is also called a Binary Indexed Tree (BIT). It was first described in a paper titled "A new data structure for cumulative frequency tables" (Peter M. Fenwick, 1994).
For the sake of simplicity, we will assume that function
Suppose we are given an array of integers,
where
The data structure is called a tree because there is a nice representation of it in the form of a tree, although we don't need to model an actual tree with nodes and edges.
We only need to maintain the array
Note: The Fenwick tree presented here uses zero-based indexing. Many people use a version of the Fenwick tree that uses one-based indexing. As such, you will also find an alternative implementation which uses one-based indexing in the implementation section. Both versions are equivalent in terms of time and memory complexity.
Now we can write some pseudo-code for the two operations mentioned above.
Below, we get the sum of elements of
def sum(int r):
res = 0
while (r >= 0):
res += t[r]
r = g(r) - 1
return res
def increase(int i, int delta):
for all j with g(j) <= i <= j:
t[j] += deltaThe function sum works as follows:
- First, it adds the sum of the range
$[g(r), r]$ (i.e.$T[r]$ ) to theresult. - Then, it "jumps" to the range
$[g(g(r)-1), g(r)-1]$ and adds this range's sum to theresult. - This continues until it "jumps" from
$[0, g(g( \dots g(r)-1 \dots -1)-1)]$ to$[g(-1), -1]$ ; this is where thesumfunction stops jumping.
The function increase works with the same analogy, but it "jumps" in the direction of increasing indices:
- The sum for each range of the form
$[g(j), j]$ which satisfies the condition$g(j) \le i \le j$ is increased bydelta; that is,t[j] += delta. Therefore, it updates all elements in$T$ that correspond to ranges in which$A_i$ lies.
The complexity of both sum and increase depend on the function
The computation of
In other words, if the least significant digit of
For instance we get
There exists a simple implementation using bitwise operations for the non-trivial operation described above:
where
Now, we just need to find a way to iterate over all
It is easy to see that we can find all such
Unsurprisingly, there also exists a simple way to perform
where
The following image shows a possible interpretation of the Fenwick tree as tree. The nodes of the tree show the ranges they cover.
Here we present an implementation of the Fenwick tree for sum queries and single updates.
The normal Fenwick tree can only answer sum queries of the type sum(int r), however we can also answer other queries of the type sum(int l, int r) method.
Also this implementation supports two constructors. You can create a Fenwick tree initialized with zeros, or you can convert an existing array into the Fenwick form.
struct FenwickTree {
vector<int> bit; // binary indexed tree
int n;
FenwickTree(int n) {
this->n = n;
bit.assign(n, 0);
}
FenwickTree(vector<int> const &a) : FenwickTree(a.size()) {
for (size_t i = 0; i < a.size(); i++)
add(i, a[i]);
}
int sum(int r) {
int ret = 0;
for (; r >= 0; r = (r & (r + 1)) - 1)
ret += bit[r];
return ret;
}
int sum(int l, int r) {
return sum(r) - sum(l - 1);
}
void add(int idx, int delta) {
for (; idx < n; idx = idx | (idx + 1))
bit[idx] += delta;
}
};The above implementation requires
The idea is, that the number
FenwickTree(vector<int> const &a) : FenwickTree(a.size()){
for (int i = 0; i < n; i++) {
bit[i] += a[i];
int r = i | (i + 1);
if (r < n) bit[r] += bit[i];
}
}Finding minimum of $[0, r]$ in one-dimensional array { data-toc-label='Finding minimum of <script type="math/tex">[0, r]</script> in one-dimensional array' }
It is obvious that there is no easy way of finding minimum of range update'd, the new value has to be smaller than the current value.
Both significant limitations are because the
struct FenwickTreeMin {
vector<int> bit;
int n;
const int INF = (int)1e9;
FenwickTreeMin(int n) {
this->n = n;
bit.assign(n, INF);
}
FenwickTreeMin(vector<int> a) : FenwickTreeMin(a.size()) {
for (size_t i = 0; i < a.size(); i++)
update(i, a[i]);
}
int getmin(int r) {
int ret = INF;
for (; r >= 0; r = (r & (r + 1)) - 1)
ret = min(ret, bit[r]);
return ret;
}
void update(int idx, int val) {
for (; idx < n; idx = idx | (idx + 1))
bit[idx] = min(bit[idx], val);
}
};Note: it is possible to implement a Fenwick tree that can handle arbitrary minimum range queries and arbitrary updates. The paper Efficient Range Minimum Queries using Binary Indexed Trees describes such an approach. However with that approach you need to maintain a second binary indexed tree over the data, with a slightly different structure, since one tree is not enough to store the values of all elements in the array. The implementation is also a lot harder compared to the normal implementation for sums.
As claimed before, it is very easy to implement Fenwick Tree for multidimensional array.
struct FenwickTree2D {
vector<vector<int>> bit;
int n, m;
// init(...) { ... }
int sum(int x, int y) {
int ret = 0;
for (int i = x; i >= 0; i = (i & (i + 1)) - 1)
for (int j = y; j >= 0; j = (j & (j + 1)) - 1)
ret += bit[i][j];
return ret;
}
void add(int x, int y, int delta) {
for (int i = x; i < n; i = i | (i + 1))
for (int j = y; j < m; j = j | (j + 1))
bit[i][j] += delta;
}
};For this approach we change the requirements and definition for
def sum(int r):
res = 0
while (r > 0):
res += t[r]
r = g(r)
return res
def increase(int i, int delta):
for all j with g(j) < i <= j:
t[j] += deltaThe computation of
The last set bit can be extracted using $i & (-i)$, so the operation can be expressed as:
And it's not hard to see, that you need to change all values
As you can see, the main benefit of this approach is that the binary operations complement each other very nicely.
The following implementation can be used like the other implementations, however it uses one-based indexing internally.
struct FenwickTreeOneBasedIndexing {
vector<int> bit; // binary indexed tree
int n;
FenwickTreeOneBasedIndexing(int n) {
this->n = n + 1;
bit.assign(n + 1, 0);
}
FenwickTreeOneBasedIndexing(vector<int> a)
: FenwickTreeOneBasedIndexing(a.size()) {
for (size_t i = 0; i < a.size(); i++)
add(i, a[i]);
}
int sum(int idx) {
int ret = 0;
for (++idx; idx > 0; idx -= idx & -idx)
ret += bit[idx];
return ret;
}
int sum(int l, int r) {
return sum(r) - sum(l - 1);
}
void add(int idx, int delta) {
for (++idx; idx < n; idx += idx & -idx)
bit[idx] += delta;
}
};A Fenwick tree can support the following range operations:
- Point Update and Range Query
- Range Update and Point Query
- Range Update and Range Query
This is just the ordinary Fenwick tree as explained above.
Using simple tricks we can also do the reverse operations: increasing ranges and querying for single values.
Let the Fenwick tree be initialized with zeros.
Suppose that we want to increment the interval add(l, x) and add(r+1, -x).
If we want to get the value of
The following implementation uses one-based indexing.
void add(int idx, int val) {
for (++idx; idx < n; idx += idx & -idx)
bit[idx] += val;
}
void range_add(int l, int r, int val) {
add(l, val);
add(r + 1, -val);
}
int point_query(int idx) {
int ret = 0;
for (++idx; idx > 0; idx -= idx & -idx)
ret += bit[idx];
return ret;
}Note: of course it is also possible to increase a single point range_add(i, i, val).
To support both range updates and range queries we will use two BITs namely
Suppose that we want to increment the interval add(B1, l, x) and add(B1, r+1, -x).
And we also update
def range_add(l, r, x):
add(B1, l, x)
add(B1, r+1, -x)
add(B2, l, x*(l-1))
add(B2, r+1, -x*r))After the range update
We can write the range sum as difference of two terms, where we use
The last expression is exactly equal to the required terms.
Thus we can use
We can find arbitrary range sums by computing the prefix sums for
def add(b, idx, x):
while idx <= N:
b[idx] += x
idx += idx & -idx
def range_add(l,r,x):
add(B1, l, x)
add(B1, r+1, -x)
add(B2, l, x*(l-1))
add(B2, r+1, -x*r)
def sum(b, idx):
total = 0
while idx > 0:
total += b[idx]
idx -= idx & -idx
return total
def prefix_sum(idx):
return sum(B1, idx)*idx - sum(B2, idx)
def range_sum(l, r):
return prefix_sum(r) - prefix_sum(l-1)- UVA 12086 - Potentiometers
- LOJ 1112 - Curious Robin Hood
- LOJ 1266 - Points in Rectangle
- Codechef - SPREAD
- SPOJ - CTRICK
- SPOJ - MATSUM
- SPOJ - DQUERY
- SPOJ - NKTEAM
- SPOJ - YODANESS
- SRM 310 - FloatingMedian
- SPOJ - Ada and Behives
- Hackerearth - Counting in Byteland
- DevSkill - Shan and String (archived)
- Codeforces - Little Artem and Time Machine
- Codeforces - Hanoi Factory
- SPOJ - Tulip and Numbers
- SPOJ - SUMSUM
- SPOJ - Sabir and Gifts
- SPOJ - The Permutation Game Again
- SPOJ - Zig when you Zag
- SPOJ - Cryon
- SPOJ - Weird Points
- SPOJ - Its a Murder
- SPOJ - Bored of Suffixes and Prefixes
- SPOJ - Mega Inversions
- Codeforces - Subsequences
- Codeforces - Ball
- GYM - The Kamphaeng Phet's Chedis
- Codeforces - Garlands
- Codeforces - Inversions after Shuffle
- GYM - Cairo Market
- Codeforces - Goodbye Souvenir
- SPOJ - Ada and Species
- Codeforces - Thor
- CSES - Forest Queries II
- Latin American Regionals 2017 - Fundraising
