“What is the -th smallest element of ?” — and its relatives: rank, count in a value range, median.

The methods

MethodPreprocessQueryMemoryUpdatesOnline
Sort the range
nth_element on a copy
Merge sort tree + binary search
Persistent segment tree
Wavelet tree bits
Offline: sort by value + BIT am.
Mo’s + a value BIT
Parallel binary search + BIT am.
BIT of sorted vectors (“BIT-of-BIT”)

The two to know: persistent segment tree (online) and the offline BIT sweep (simpler, less memory).

Persistent segment tree — the online answer

Build version by inserting into version , with the tree indexed by value. Then version minus version is a segment tree over exactly the values in , and descending it finds the -th smallest.

int kth(int u, int v, int lo, int hi, int k) {         // u = root[l-1], v = root[r]
    if (lo == hi) return lo;
    int leftCount = cnt[left[v]] - cnt[left[u]];
    int mid = (lo + hi) / 2;
    if (k <= leftCount) return kth(left[u],  left[v],  lo, mid, k);
    else                return kth(right[u], right[v], mid+1, hi, k - leftCount);
}

per query, nodes. Compress the values first.

The same structure answers:

  • count of elements in ,
  • count in a value range,
  • sum of the smallest (store sums alongside counts),
  • the median (with ).

Offline: sort by value and sweep

If the queries are known in advance, this is simpler and uses memory:

// binary search the answer VALUE for all queries in parallel
// (parallel binary search), with a BIT counting positions of inserted values

Or, for “count in ” specifically, sort both the array elements and the queries by value and sweep with a BIT over positions — time, memory. See Offline Query Processing.

With updates

Point updates plus -th-smallest queries need either:

  • BIT of segment trees (a “2D” structure), per operation, memory — heavy;
  • parallel binary search offline, amortized, memory — much lighter;
  • sqrt decomposition with sorted blocks, per query, to rebuild a block on update.

The offline route is nearly always the right choice in a contest.

The whole array, not a range

For “-th smallest of the entire array”:

nth_element(a.begin(), a.begin() + k, a.end());        // O(n) expected
int kth = a[k];

expected with introselect. For a guaranteed , median-of-medians exists but is slower in practice.

QueryMethod
Rank of in count of elements
Median of
Count in the value range difference of two counts
Sum of the smallestpersistent segtree storing sums
Mode (most frequent) of different problem — offline, or an preprocessing
-th smallest on a tree pathpersistent segment tree indexed by root-path, combined with LCA
-th distinct valuea distinct-count structure plus a descent

The tree-path version is a nice combination: build a persistent segment tree along each root-to-vertex path, then the multiset on the path is where is the LCA — four versions descended in lockstep.

See also: Persistent Structures · Wavelet Tree · Parallel Binary Search