Alex's Anthology of Algorithms Common Code for Contests in Concise C++
Data Structures / Range Queries in One Dimension

A merge sort tree is a static segment tree in which every node stores the sorted multiset of the values in its range. The nodes are exactly the ranges visited by a merge sort, and each node's sorted list is the merge of its two children's lists. This supports queries that count, within a range of indices, how many values fall below a threshold or inside a value interval: decompose the index range into $O(\log n)$ canonical nodes and binary search each node's sorted list.

The structure is built once and not modified afterward. All index ranges are inclusive $[{\htmlClass{math-inline-code}{\texttt{lo}}}, {\htmlClass{math-inline-code}{\texttt{hi}}}]$ with 0-based indices, and values may be of any comparable type.

  • MergeSortTree<T>(a) builds the tree over the array a.
  • size() returns the size of the array.
  • count_leq(lo, hi, x) returns the number of indices i in $[{\htmlClass{math-inline-code}{\texttt{lo}}}, {\htmlClass{math-inline-code}{\texttt{hi}}}]$ with a[i] $\leq$ x.
  • count_in(lo, hi, x, y) returns the number of indices i $\in [{\htmlClass{math-inline-code}{\texttt{lo}}}, {\htmlClass{math-inline-code}{\texttt{hi}}}]$ such that a[i] $\in [{\htmlClass{math-inline-code}{\texttt{x}}}, {\htmlClass{math-inline-code}{\texttt{y}}}]$.

Implementation

#include <algorithm>
#include <cassert>
#include <iterator>
#include <vector>

template<typename T>
class MergeSortTree {
  int len;
  std::vector<std::vector<T>> tree;

  void build(int i, int lo, int hi, const std::vector<T> &a) {
    if (lo == hi) {
      tree[i] = {a[lo]};
      return;
    }
    int mid = lo + (hi - lo) / 2;
    build(i * 2, lo, mid, a);
    build(i * 2 + 1, mid + 1, hi, a);
    std::merge(
        tree[i * 2].begin(), tree[i * 2].end(), tree[i * 2 + 1].begin(), tree[i * 2 + 1].end(),
        std::back_inserter(tree[i])
    );
  }

  template<typename Fn>
  int query(int i, int lo, int hi, int tgt_lo, int tgt_hi, const Fn &count_node) const {
    if (tgt_hi < lo || hi < tgt_lo) {
      return 0;
    }
    if (tgt_lo <= lo && hi <= tgt_hi) {
      return count_node(tree[i]);
    }
    int mid = lo + (hi - lo) / 2;
    return query(i * 2, lo, mid, tgt_lo, tgt_hi, count_node) +
           query(i * 2 + 1, mid + 1, hi, tgt_lo, tgt_hi, count_node);
  }

 public:
  explicit MergeSortTree(const std::vector<T> &a) : len(a.size()), tree(4 * a.size()) {
    assert(len > 0);
    build(1, 0, len - 1, a);
  }

  int size() const { return len; }

  int count_leq(int lo, int hi, const T &x) const {
    assert(0 <= lo && lo <= hi && hi < len);
    return query(1, 0, len - 1, lo, hi, [&](const std::vector<T> &v) {
      return static_cast<int>(std::upper_bound(v.begin(), v.end(), x) - v.begin());
    });
  }

  int count_in(int lo, int hi, const T &x, const T &y) const {
    assert(0 <= lo && lo <= hi && hi < len);
    assert(!(y < x));
    return query(1, 0, len - 1, lo, hi, [&](const std::vector<T> &v) {
      return static_cast<int>(
          std::upper_bound(v.begin(), v.end(), y) - std::lower_bound(v.begin(), v.end(), x)
      );
    });
  }
};

Example Usage

using namespace std;

int main() {
  vector<int> a{5, 2, 8, 6, 1, 9, 3};
  MergeSortTree<int> t(a);

  assert(t.size() == 7);
  assert(t.count_leq(0, 6, 5) == 4);    // 5, 2, 1, 3
  assert(t.count_leq(2, 4, 6) == 2);    // 6, 1
  assert(t.count_in(0, 6, 3, 8) == 4);  // 5, 8, 6, 3
  assert(t.count_in(1, 5, 2, 6) == 2);  // 2, 6 (8, 1, 9 excluded)
  return 0;
}