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

Maintain a static set of two-dimensional points while supporting rectangle reporting queries. A k-d tree recursively splits points by alternating coordinates and stores a bounding box for each subtree, allowing whole subtrees to be accepted or pruned during a query.

This implementation uses std::pair to represent points, requiring operator< to be defined on the numeric template type.

Use this for static point-reporting queries when $O(n)$ space and good average performance are more important than a strict worst-case guarantee. Use the 2D range tree instead when adversarial point sets or query rectangles are expected and the extra $O(n \log n)$ space is acceptable.

  • RangeKDTree<T>(lo, hi) constructs a set of std::pair points from the half-open forward-iterator range $[{\htmlClass{math-inline-code}{\texttt{lo}}}, {\htmlClass{math-inline-code}{\texttt{hi}}})$.
  • query(x1, y1, x2, y2, f) calls the function f(p) on each point whose $x$-coordinate is in $[{\htmlClass{math-inline-code}{\texttt{x1}}}, {\htmlClass{math-inline-code}{\texttt{x2}}}]$ and whose $y$-coordinate is in $[{\htmlClass{math-inline-code}{\texttt{y1}}}, {\htmlClass{math-inline-code}{\texttt{y2}}}]$. The argument p is the point as an std::pair.

Implementation

#include <algorithm>
#include <cassert>
#include <utility>
#include <vector>

template<typename T>
class RangeKDTree {
  std::vector<std::pair<T, T>> tree, minp, maxp;
  std::vector<int> l_index, h_index;

  void build(int lo, int hi, bool div_x) {
    if (lo >= hi) {
      return;
    }
    int mid = lo + (hi - lo) / 2;
    std::nth_element(
        tree.begin() + lo, tree.begin() + mid, tree.begin() + hi,
        [div_x](const auto &a, const auto &b) {
          return div_x ? a.first < b.first : a.second < b.second;
        }
    );
    l_index[mid] = lo;
    h_index[mid] = hi;
    minp[mid].first = maxp[mid].first = tree[lo].first;
    minp[mid].second = maxp[mid].second = tree[lo].second;
    for (int i = lo + 1; i < hi; i++) {
      minp[mid].first = std::min(minp[mid].first, tree[i].first);
      minp[mid].second = std::min(minp[mid].second, tree[i].second);
      maxp[mid].first = std::max(maxp[mid].first, tree[i].first);
      maxp[mid].second = std::max(maxp[mid].second, tree[i].second);
    }
    build(lo, mid, !div_x);
    build(mid + 1, hi, !div_x);
  }

  template<typename Fn>
  void query(int lo, int hi, const T &x1, const T &y1, const T &x2, const T &y2, Fn &f) {
    if (lo >= hi) {
      return;
    }
    int mid = lo + (hi - lo) / 2;
    T ax = minp[mid].first, ay = minp[mid].second;
    T bx = maxp[mid].first, by = maxp[mid].second;
    if (x2 < ax || bx < x1 || y2 < ay || by < y1) {
      return;
    }
    if (!(ax < x1 || x2 < bx || ay < y1 || y2 < by)) {
      for (int i = l_index[mid]; i < h_index[mid]; i++) {
        f(tree[i]);
      }
      return;
    }
    query(lo, mid, x1, y1, x2, y2, f);
    query(mid + 1, hi, x1, y1, x2, y2, f);
    if (tree[mid].first < x1 || x2 < tree[mid].first || tree[mid].second < y1 ||
        y2 < tree[mid].second) {
      return;
    }
    f(tree[mid]);
  }

 public:
  template<typename It>
  RangeKDTree(It lo, It hi)
      : tree(lo, hi),
        minp(tree.size()),
        maxp(tree.size()),
        l_index(tree.size()),
        h_index(tree.size()) {
    build(0, static_cast<int>(tree.size()), true);
  }

  template<typename Fn>
  void query(const T &x1, const T &y1, const T &x2, const T &y2, Fn f) {
    assert(!(x2 < x1) && !(y2 < y1));
    query(0, static_cast<int>(tree.size()), x1, y1, x2, y2, f);
  }
};

Example Usage

#include <algorithm>
#include <cassert>
using namespace std;

int main() {
  vector<pair<int, int>> v{{1, 4},  {5, 4},  {2, 2},   {3, 1},   {6, -5},
                           {5, -1}, {3, -3}, {-1, -2}, {-1, -1}, {2, -1}};
  RangeKDTree<int> t(v.begin(), v.end());
  vector<pair<int, int>> got;
  auto collect = [&](const pair<int, int> &p) { got.push_back(p); };
  t.query(-1, -1, 2, 5, collect);
  sort(got.begin(), got.end());
  assert((got == vector<pair<int, int>>{{-1, -1}, {1, 4}, {2, -1}, {2, 2}}));
  got.clear();
  t.query(1, 1, 4, 8, collect);
  sort(got.begin(), got.end());
  assert((got == vector<pair<int, int>>{{1, 4}, {2, 2}, {3, 1}}));
  return 0;
}