A segment tree stores an associative aggregate for each of the canonical segments. Any range splits into of them. All the power is in what a node stores: if two adjacent segments’ summaries can be combined into the summary of their union, a segment tree answers range queries over it.

Bottom-up monoid tree (AtCoder style)

template<class S, S (*op)(S, S), S (*e)()> struct SegTree {
    int n, size; vector<S> t;
    SegTree(const vector<S>& a) : n(sz(a)), size(1) {
        while (size < n) size <<= 1;
        t.assign(2 * size, e());
        copy(all(a), t.begin() + size);
        for (int i = size - 1; i > 0; i--) t[i] = op(t[2 * i], t[2 * i + 1]);
    }
    void set(int p, S v) { for (t[p += size] = v; p >>= 1; ) t[p] = op(t[2 * p], t[2 * p + 1]); }
    S get(int p) const { return t[p + size]; }
    S prod(int l, int r) const {                       // [l, r), keeps left-to-right order
        S sl = e(), sr = e();
        for (l += size, r += size; l < r; l >>= 1, r >>= 1) {
            if (l & 1) sl = op(sl, t[l++]);
            if (r & 1) sr = op(t[--r], sr);
        }
        return op(sl, sr);
    }
    template<class F> int maxRight(int l, F f) const { // largest r with f(prod(l, r)) true; f(e()) must be true
        if (l == n) return n;
        l += size; S acc = e();
        do {
            while (l % 2 == 0) l >>= 1;
            if (!f(op(acc, t[l]))) {
                while (l < size) { l = 2 * l; if (f(op(acc, t[l]))) acc = op(acc, t[l++]); }
                return l - size;
            }
            acc = op(acc, t[l++]);
        } while ((l & -l) != l);
        return n;
    }
    template<class F> int minLeft(int r, F f) const {  // smallest l with f(prod(l, r)) true
        if (r == 0) return 0;
        r += size; S acc = e();
        do {
            r--;
            while (r > 1 && (r % 2)) r >>= 1;
            if (!f(op(t[r], acc))) {
                while (r < size) { r = 2 * r + 1; if (f(op(t[r], acc))) acc = op(t[r--], acc); }
                return r + 1 - size;
            }
            acc = op(t[r], acc);
        } while ((r & -r) != r);
        return 0;
    }
};
  • op only needs to be associative. The left and right partial results are kept apart, so non-commutative monoids (matrices, function composition, strings) work.
  • maxRight / minLeft are binary searches on the tree in : “first position where the prefix sum from exceeds ”, “longest segment starting at with gcd ”, “nearest element to the left that is ” (with max).
  • It’s about 2x faster than a recursive tree and uses nodes.

A recursive tree is easier to extend with lazy propagation, dynamic nodes and custom descents; see Lazy Propagation.

Node design

QueryNode storesMerge
max subarray sum in a rangesum, best prefix, best suffix, bestsee Kadane
min and how many times it occurs(min, count)add counts on ties
longest run of equal values / of oneslength, prefix run, suffix run, best, end valuesjoin runs across the middle
balanced bracket subsequence length(matched, open, close)m = min(L.open, R.close)
is the range a valid bracket sequence(sum, min prefix)prefix min of the right part shifted by the left sum
range of a linear recurrence / DP transitiona matrixmatrix product (order matters)
composition of functions composition
palindrome check of a substring with updatesforward and backward polynomial hashshift and add
number of inversions in a range of a permutation (static)merge sort tree or wavelet
”LIS-like” or “visible from the left” countsmax and the answer with a helper descent” pull” segment tree (the classic visible-buildings problem)
distinct values countnot mergeable: offline sweep or persistence
mode / majorityBoyer-Moore pair (candidate, count)candidate survives with count difference

pull technique: when the merge needs a query into the right child (“count prefix maxima in the right child that exceed the left child’s max”), implement it as a descent of the right child that is . Then updates cost . Classic for “number of visible buildings with point updates”.

Other forms

  • Segment tree over values (counts per value): -th smallest, rank, and “sum of the smallest” by descent; with compression or as a dynamic tree over .
  • Segment tree on a trie of indices (XOR on indices): to apply to all positions and query sums, note that XOR with bit swaps the two children at the level of bit . Keep a lazy XOR mask per node and swap children when the mask has the level’s bit. A cheaper offline trick: precompute, for every node and every mask of its levels, the aggregate ( total).
  • Segment tree of sorted vectors (merge sort tree): count in in (Order Statistics).
  • Segment tree on a tree: via Euler tour (subtrees) or HLD (paths).
  • Segment tree of segment trees (2D): point update and rectangle query for any monoid in , memory dense or dynamic. No lazy propagation across the outer dimension.
  • Segment tree over time / over queries: offline deletion (Offline Techniques), and segment tree graph construction for range edges (segment tree graph).
  • Dynamic / sparse (nodes created on demand) and persistent (old versions kept): when the coordinate range is too large to allocate upfront, or past versions must be queryable — see Dynamic and Persistent Segment Trees.
  • Beats (chmin/chmax with range sum): when a node’s aggregate can’t be updated in from a chmin/chmax tag alone — see Segment Tree Beats.

Fenwick or segment tree?

Use a Fenwick tree for invertible operations with point updates (smaller and faster). Use a segment tree for non-invertible operations (min, max, gcd), complex nodes, descents, or lazy range updates.

FenwickSegment tree
code~15 linesmore boilerplate
memory recursive, bottom-up
operationmainly invertible (sum, xor)any associative op
range update + range queryneeds two Fenwicks (trick)native, via lazy propagation
min/maxprefix only, monotone updatesfully native
chmin/k-th/persistencenot designed for itdedicated variants exist (Beats, persistent, merge sort tree)
constant factorsmallerlarger, far more general

Pitfalls

  • The identity e() must truly be neutral: for min use , for gcd use 0, for matrices the identity matrix.
  • Recursive trees need about nodes. Bottom-up trees need the next power of two (or with the non-power-of-two layout, which breaks order for non-commutative ops and the descents).
  • Out-of-range queries with should return e().