# Fenwick & Segment Trees - Templates + Applications
## Why These Structures Are Awesome
* Let you go from $O(n^2)$ brute → $O(n \log n)$ (this is the core win)
* Handle **“compare current with all before it”** type problems
* Reusable templates: just swap what you store (counts, sums, max, gcd, etc)
* Scale comfortably to $n \approx 10^5$–$2 \times 10^5$
But they’re **not always needed**.
If the problem is just static prefix sums (like subarray sum existence), a simple prefix array is $O(1)$ per query and way simpler.
Fenwick/Segtree is only worth it if you need *updates + queries* or dynamic sweep-style logic.
---
## What To Keep In Mind
* **Constraints**:
* $n \approx 10^5$: $O(n \log n)$ is fine
* $n \approx 10^6+$: log factors start to hurt → maybe you need a math identity, greedy, or offline trick instead of a Fenwick/Segtree
* **Coordinate Compression**:
* If values are large (like up to $10^9$), compress into $[1..n]$ before storing in Fenwick/Segtree
* **Memory**:
* Textbook Segment Tree uses about $4n$
* Fenwick Tree uses $n$
* Iterative/compact Segment Tree can be closer to $2n$
* Disclaimer: Segment Tree will always be heavier than Fenwick
---
## When + Where To Use
**Longest Increasing Subsequence (LIS)**
* Solve in $O(n \log n)$ with a Segtree/Fenwick
* State:
$$
dp[i] = 1 + \max_{\;a[j] < a[i]} dp[j]
$$
* Query for max over $[1, a[i]-1]$, then update at $a[i]$
**Inversion Counting**
* Classic Fenwick application
* Count pairs $(i, j)$ with $i < j$ and $a[i] > a[j]$
* Store **counts** in Fenwick
**Weighted Inversion / Difference Problems**
* General class of problems where contribution depends on both the **count** and the **sum** of earlier elements
* Example: for each $a[i]$, add/subtract differences against all previous elements
* Solution: maintain 2 Fenwicks → one for counts, one for sums
**Manhattan Distance Problems (special cases)**
* expand $|x_i - x_j| + |y_i - y_j|$ into linear terms involving counts + sums
* sweep one axis, store the other in Fenwicks
* only shows up in certain geometry/distance problems, not every distance task
**Line Sweep**
* Process events in sorted order (by coordinate or time)
* Maintain “active set” with Fenwick/Segtree
* Shows up in rectangle union, closest pairs, scheduling, etc
---
## Disclaimer
These are **general templates**.
You always tweak what you store (counts, sums, max, gcd, etc) to match the problem.
Don’t overkill, if it’s just prefix sums & the constraints are big enough, use a plain array.
---
## Fenwick Tree (BIT)
```cpp
struct Fenwick {
int n;
vector<long long> bit;
Fenwick(int n) : n(n), bit(n+1,0) {}
void add(int i, long long x) {
for (; i <= n; i += i & -i) bit[i] += x;
}
long long sum(int i) {
long long res = 0;
for (; i > 0; i -= i & -i) res += bit[i];
return res;
}
long long rangeSum(int l, int r) {
return sum(r) - sum(l-1);
}
};
```
**Notes:**
* Fenwick = **point update + prefix/range query**.
* Always use `long long` for sums — `n * max(a[i])` can exceed `2e9`.
* If you only store counts (`+1` each time), `int` would be fine, but `ll` is safer universally.
---
## Segment Tree
```cpp
class SegmentTree {
private:
int n;
vector<int> tree;
public:
SegmentTree(int sz) {
n = 1;
while (n < sz) n <<= 1;
tree.assign(2 * n, 0); // iterative style (~2n space)
}
void update(int i, int val) {
i += n;
tree[i] = max(tree[i], val);
while (i > 1) {
i >>= 1;
tree[i] = max(tree[i << 1], tree[i << 1 | 1]);
}
}
int query(int l, int r) {
l += n, r += n;
int res = 0;
while (l <= r) {
if (l & 1) res = max(res, tree[l++]);
if (!(r & 1)) res = max(res, tree[r--]);
l >>= 1, r >>= 1;
}
return res;
}
};
```
**Notes:**
* This template is tuned for **max queries** (like LIS DP).
* For **sum segment tree**, change the merge op (`+`) and the type to `long long`.
* Point update here is **assign/merge at one index**; range queries aggregate from leaves up.
---
## Templated Segment Tree
```cpp
```
## Example: Weighted Inversion Problem
**Problem (generalized):**
For each element $a[i]$, compute
$$
\text{counter}[i] =
\sum_{j<i,\;a[j]<a[i]} (a[i] - a[j])
\;-\;
\sum_{j<i,\;a[j]>a[i]} (a[j] - a[i])
$$
In other words, for each element `a[i]`, look back at all earlier elements `a[j]`:
* If `a[j] < a[i]`, add the gap `(a[i] - a[j])`.
* If `a[j] > a[i]`, subtract the gap `(a[j] - a[i])`.
So `counter[i]` measures **how much bigger `a[i]` is than the smaller numbers before it, `a[j]`, minus how much smaller it is than the bigger numbers before it.**.
Brute force is $O(n^2)$, which dies at $n = 10^5$.
**Observation:**
This is basically inversion counting with weights:
* normal inversion = just counts
* here = counts + sums
We use two Fenwick Wrees
* One to maintain count of numbers seen so far
* One to maintain sum of numbers seen so far
### Coordinate Compression + Ranking
Fenwick trees only support **prefix queries** (sums over `[1..k]`).
To turn “smaller than `a[i]`” into a prefix query, we:
1. **Compress values**: map each unique value to a rank (1 = smallest, m = largest).
* Example: arr = `[3,1,4]` → ranks `{1→1, 3→2, 4→3}`.
2. Then:
* `cnt.sum(idx-1)` = # of elements with rank ≤ `idx-1` = **all elements smaller than `a[i]`**.
* `sum.sum(idx-1)` = sum of all elements with rank ≤ `idx-1` = **sum of smaller values**.
* `cnt.sum(m) - cnt.sum(idx)` = # greater.
* `sum.sum(m) - sum.sum(idx)` = sum of greater values.
So the whole “smaller/greater” logic reduces to simple prefix sums thanks to ranking.
---
### Formula
At each index $i$:
$$
\text{counter}[i] =
\big(a[i] \cdot \text{cntSmaller} - \text{sumSmaller}\big)
+
\big(\text{sumGreater} - a[i] \cdot \text{cntGreater}\big)
$$
Where:
* `cntSmaller = cnt.sum(idx-1)`
* `sumSmaller = sum.sum(idx-1)`
* `cntGreater = cnt.sum(m) - cnt.sum(idx)`
* `sumGreater = sum.sum(m) - sum.sum(idx)`
**Code:**
```cpp
vector<long long> solve(vector<int>& arr) {
int n = arr.size();
// coordinate compression
vector<int> vals = arr;
sort(vals.begin(), vals.end());
vals.erase(unique(vals.begin(), vals.end()), vals.end());
auto get = [&](int x) {
return int(lower_bound(vals.begin(), vals.end(), x) - vals.begin()) + 1;
};
int m = vals.size();
Fenwick cnt(m), sum(m);
vector<long long> ans(n);
for (int i = 0; i < n; i++) {
int idx = get(arr[i]);
long long cntSmaller = cnt.sum(idx-1);
long long sumSmaller = sum.sum(idx-1);
long long cntAll = cnt.sum(m);
long long sumAll = sum.sum(m);
long long cntGreater = cntAll - cnt.sum(idx);
long long sumGreater = sumAll - sum.sum(idx);
long long res = arr[i]*cntSmaller - sumSmaller;
res += sumGreater - arr[i]*cntGreater;
ans[i] = res;
cnt.add(idx, 1);
sum.add(idx, arr[i]);
}
long long total = 0;
for (int i = 0; i < n; i++) {
total += ans[i];
}
return total;
}
```
---
# Quick Cheat Table
| Problem type | What to store in tree |
| ----------------------------- | ------------------------------ |
| Inversion counting | Counts only |
| LIS (dp style) | Max DP value |
| Weighted inversions/diffs | Counts + Sums |
| Manhattan distance (2D sweep) | Counts + Sums (sweep one axis) |
| Range sum query | Prefix Sums |
| Range min/max/gcd | Segtree with Merge Op |
## Recommended problems
* [USACO Guide – Longest Increasing Subsequence](https://usaco.guide/gold/lis)
* [USACO Guide – Point Update Range Sum](https://usaco.guide/gold/PURS)
* [USACO Guide – Range Query Section from Plat](https://usaco.guide/plat/)