Fenwick
Introduction¶
The Fenwick tree(or, binary indexed tree) and the segment tree are always mentioned together, but there are still some differences between them after all:
The operations that Fenwick tree can have, the segment tree must have; The operations that segment tree have, the Fenwick tree does not necessarily have.
So it seems that choosing segment tree would be the best choice?
In fact, the code of Fenwick tree is much shorter, and the principle is clearer. When solving some single-node modification problems, the Fenwick tree is the best choice.
Principle¶
If you want to understand the principle of the fenwick tree, please refer to the following figure:
The idea of this structure is somewhat similar to the segment tree: a large node is used to represent the information of some small nodes, and only some large nodes are required to be queried instead of more small nodes.
The bottom eight squares represent the eight numbers stored in
The remaining uneven squares above represent the parent of
Obviously we can find:
Then the principle of continuously jumping to the center nodes and scores keep increasing is the same (multiplication).
So, if you want to calculate the sum of intervals, for example, the sum of intervals from
Then the principle of continuously jumping to the center node, which is similar to jumping to the center point, is the same (multiplication).
You jump forward from
Usage and operation¶
So the question is, how do you know the number of lowbit
:
int lowbit(int x) {
// find the decimal number corresponding to the binary number of the first 1 and the 0 after this 1 from right to left of x
return x & -x;
}
The meaning of lowbit
is explained, let’s use this statement to prove it
Found that the binary composed of the first
The decimal system corresponding to
So this is what the lowbit
is used for.
You might be wondering: what does x & -x mean?
In general, for a positive number of int type, the highest bit is 0, followed by its binary representation; for negative numbers (-x), the representation method is to invert x and add 1 bitwise.
For example:
Then it is easier for single node modification:
void add(int x, int k) {
while (x <= n) { // cannot cross the boundary
c[x] = c[x] + k;
x = x + lowbit(x);
}
}
Every time updating from the parent, then you can leave it alone.
int getsum(int x) { // sum of a[1] ... a[x]
int ans = 0;
while (x >= 1) {
ans = ans + c[x];
x = x - lowbit(x);
}
return ans;
}
Interval addition & interval sum¶
If we maintain the finite difference array
Derive:
The interval sum can be obtained by subtracting two prefix sums, so we only need two fenwick trees to maintain
Code is shown below:
int t1[MAXN], t2[MAXN], n;
inline int lowbit(int x) { return x & (-x); }
void add(int k, int v) {
int v1 = k * v;
while (k <= n) {
t1[k] += v, t2[k] += v1;
k += lowbit(k);
}
}
int getsum(int *t, int k) {
int ret = 0;
while (k) {
ret += t[k];
k -= lowbit(k);
}
return ret;
}
void add1(int l, int r, int v) {
add(l, v), add(r + 1, -v); // split the interval sum into two prefix sum
}
long long getsum1(int l, int r) {
return (r + 1ll) * getsum(t1, r) - 1ll * l * getsum(t1, l - 1) -
(getsum(t2, r) - getsum(t2, l - 1));
}
Tricks¶
build the tree in
The value of each node is obtained by summing the values of all the children nodes directly connected to it. Therefore, you can consider the contribution backwards, that is, every time you determine the value of the child, update the direct parent with your own value.
// O(n) build the tree
void init() {
for (int i = 1; i <= n; ++i) {
t[i] += a[i];
int j = i + lowbit(i);
if (j <= n) t[j] += t[i];
}
}
Query the
Refer to the "Persistent Segment Tree" chapter for the idea of finding the smallest
Therefore, we can think of the algorithm: if we have found that
In the Fenwick tree, the nodes are divided according to the power of 2, which can be expanded each time. Let
- Find
depth=\left \lfloor \log_2n \right \rfloor - Calculate
t=\sum_{i=x+1}^{x+2^{depth}}a_i - If
sum+t \le k 2^{depth} x x - Decrease
depth depth
// Weighted fenwick tree query k-th smallest
int kth(int k) {
int cnt = 0, ret = 0;
for (int i = log2(n); ~i; --i) { // i has the same meaning as depth above
ret += 1 << i; // Try to expand
if (ret >= n || cnt + t[ret] >= k) // If the expansion fails
ret -= 1 << i;
else
cnt += t[ret]; // After the expansion is successful, the previously summed value must be updated
}
return ret + 1;
}
Timestamp optimization:
A common technique for dealing with multiple sets of data. If the Fenwick tree is brute-forece cleared every time new data comes in, it may cause a timeout. Therefore, the
// Timestamp optimization
int tag[MAXN], t[MAXN], Tag;
void reset() { ++Tag; }
void add(int k, int v) {
while (k <= n) {
if (tag[k] != Tag) t[k] = 0;
t[k] += v, tag[k] = Tag;
k += lowbit(k);
}
}
int getsum(int k) {
int ret = 0;
while (k) {
if (tag[k] == Tag) ret += t[k];
k -= lowbit(k);
}
return ret;
}
Sample problems¶
Note: all original problems are in Chinese.
- Fenwick tree 1: single node modification, interval query
- Fenwick tree 2: interval modification, single node query
- Fenwick tree 3: interval modification, interval query
- 2D Fenwick tree 1: single node modification, interval query
- 2D Fenwick tree 3: interval modification, interval query
buildLast update and/or translate time of this article,Check the history
editFound smelly bugs? Translation outdated? Wanna contribute with us? Edit this Page on Github
peopleContributor of this article HeRaNO, Zhoier, Ir1d, Xeonacid, wangdehu, ouuan, ranwen, ananbaobeichicun, Ycrpro
translateTranslator of this article Visit the original article!
copyrightThe article is available under CC BY-SA 4.0 & SATA ; additional terms may apply.