Queries over a grid or a point set: rectangle sums, counts, dominance. The choice of structure depends almost entirely on whether the queries can go offline.

The decision table

GridUpdatesOnline?StructureCost
Dense, none2D prefix sum query
Dense, point2D BIT
Denserectangle add + rectangle sumfour 2D BITs
Sparse, largenoneoffline sweep + BIT
Sparse, largenonepersistent segment tree
Sparse, largepointoffline sweep, or CDQ
Sparse, largepointsparse 2D segment tree, or KD-tree /
3+ dimensionsanyCDQ, nested
Non-rectangular regionsKD-tree

Go offline

The single most useful fact here: sort by one coordinate and sweep with a 1D BIT answers most 2D query problems in time and memory — better than any 2D structure on both axes. Reach for a genuinely 2D structure only when the queries must be online.

The offline sweep

Decompose each rectangle query into two (or four) prefix queries by inclusion-exclusion, then sort everything by :

// count points inside each query rectangle
struct Ev { int x, y1, y2, id, sign; bool isPoint; };
sort(ev.begin(), ev.end(), [](const Ev& a, const Ev& b) {
    return a.x != b.x ? a.x < b.x : a.isPoint > b.isPoint;   // points before queries
});
BIT bit(maxY);
for (auto& e : ev) {
    if (e.isPoint) bit.add(e.y1, 1);
    else ans[e.id] += e.sign * bit.rangeSum(e.y1, e.y2);
}

The tie-break (points before queries at the same ) encodes whether the boundary is inclusive — get it wrong and off-by-one errors appear only on specific inputs.

2D prefix sums


The four-term formula is inclusion-exclusion; in dimensions it becomes terms. Beyond the constant makes it impractical.

The 2D difference array is the dual (rectangle update, read at the end) — see Difference Array.

Dominance counting

“For each point, how many others have both coordinates it?”

Sort by , sweep, and count with a BIT over . .

In three dimensions this becomes the 3D partial order problem, solved by CDQ in — sort by , CDQ on , BIT on .

Rectangle union

Area, perimeter, or “how many rectangles cover each point”: a sweep line with a segment tree storing per-node counts and covered length. . This tree needs no lazy propagation, because updates arrive in matching pairs.

KD-trees and their niche

A KD-tree answers rectangle queries in and nearest-neighbour in expected, with memory, and supports insertions with periodic rebuilds.

Its real advantage is non-rectangular regions — circles, half-planes, arbitrary convex shapes — which BIT-based sweeps cannot express. For plain rectangles, the offline sweep wins.

Memory, the real constraint

StructureMemory
2D prefix / 2D BIT
Merge sort tree / BIT of vectors
Persistent segment tree ints
Offline sweep + BIT
KD-tree
2D segment tree (sparse)

A long long 2D BIT is 32 MB — near the limit. Above that, compress or go offline.

Higher dimensions

Each extra dimension costs a (CDQ) or makes range queries (KD-tree). Beyond or 4, essentially every structure degenerates to a linear scan, and the right move is to find a reformulation that removes a dimension — often by sorting.

See also: 2D Fenwick · CDQ · Sweep Line