Range Updates & Queries
Fenwick Tree (Binary Indexed Trees)
- Supports efficient prefix sums (range sum queries from index 1 to ) and point updates.
- Internally stores cumulative frequencies in a vector ft.
Simple Implementation
#include <vector>
using namespace std;
class FenwickTree {
private:
vector<long long> ft;
int LSOne(int s) { return s & -s; }
public:
FenwickTree(int n) {
ft.assign(n + 1, 0); // 1-based indexing
}
// Point update: add v to index i
void update(int i, long long v) {
while (i < (int)ft.size()) {
ft[i] += v;
i += LSOne(i);
}
}
// Prefix sum query: sum[1..i]
long long rsq(int i) {
long long sum = 0;
while (i > 0) {
sum += ft[i];
i -= LSOne(i);
}
return sum;
}
// Range sum query: sum[i..j]
long long rsq(int i, int j) {
return rsq(j) - rsq(i - 1);
}
};
Complete Implementation
#include <bits/stdc++.h>
using namespace std;
#define LSOne(S) ((S) & -(S)) // the key operation
typedef long long ll; // for extra flexibility
typedef vector<ll> vll;
typedef vector<int> vi;
class FenwickTree { // index 0 is not used
private:
vll ft; // internal FT is an array
public:
FenwickTree(int m) { ft.assign(m+1, 0); } // create an empty FT
void build(const vll &f) {
int m = (int)f.size()-1; // note f[0] is always 0
ft.assign(m+1, 0);
for (int i = 1; i <= m; ++i) { // O(m)
ft[i] += f[i]; // add this value
if (i+LSOne(i) <= m) // i has parent
ft[i+LSOne(i)] += ft[i]; // add to that parent
}
}
FenwickTree(const vll &f) { build(f); } // create FT based on f
FenwickTree(int m, const vi &s) { // create FT based on s
vll f(m+1, 0);
for (int i = 0; i < (int)s.size(); ++i) // do the conversion first
++f[s[i]]; // in O(n)
build(f); // in O(m)
}
ll rsq(int j) { // returns RSQ(1, j)
ll sum = 0;
for (; j; j -= LSOne(j))
sum += ft[j];
return sum;
}
ll rsq(int i, int j) { return rsq(j) - rsq(i-1); } // inc/exclusion
// updates value of the i-th element by v (v can be +ve/inc or -ve/dec)
void update(int i, ll v) {
for (; i < (int)ft.size(); i += LSOne(i))
ft[i] += v;
}
int select(ll k) { // O(log m)
int p = 1;
while (p*2 < (int)ft.size()) p *= 2;
int i = 0;
while (p) {
if (k > ft[i+p]) {
k -= ft[i+p];
i += p;
}
p /= 2;
}
return i+1;
}
};
Range Update Point Query & Range Query
- Supports range updates & point queries
- Uses a Fenwick Tree internally but updates the Fenwick Tree in a way that a range update is simulated by two point updates.
class RUPQ { // RUPQ variant
private:
FenwickTree ft; // internally use PURQ FT
public:
RUPQ(int m) : ft(FenwickTree(m)) {}
void range_update(int ui, int uj, ll v) {
ft.update(ui, v); // [ui, ui+1, .., m] +v
ft.update(uj+1, -v); // [uj+1, uj+2, .., m] -v
} // [ui, ui+1, .., uj] +v
ll point_query(int i) { return ft.rsq(i); } // rsq(i) is sufficient
};
class RURQ { // RURQ variant
private: // needs two helper FTs
RUPQ rupq; // one RUPQ and
FenwickTree purq; // one PURQ
public:
RURQ(int m) : rupq(RUPQ(m)), purq(FenwickTree(m)) {} // initialization
void range_update(int ui, int uj, ll v) {
rupq.range_update(ui, uj, v); // [ui, ui+1, .., uj] +v
purq.update(ui, v*(ui-1)); // -(ui-1)*v before ui
purq.update(uj+1, -v*uj); // +(uj-ui+1)*v after uj
}
ll rsq(int j) {
return rupq.point_query(j)*j - // optimistic calculation
purq.rsq(j); // cancelation factor
}
ll rsq(int i, int j) { return rsq(j) - rsq(i-1); } // standard
};