Range queries over a grid. The structures generalise, but the memory grows quickly, so choose carefully.

2D Fenwick tree

A BIT of BITs. Point update, prefix-rectangle query, both .

struct BIT2D {
    int n, m;
    vector<vector<long long>> t;
    BIT2D(int n, int m) : n(n), m(m), t(n + 1, vector<long long>(m + 1, 0)) {}
 
    void update(int x, int y, long long v) {            // 1-indexed
        for (int i = x; i <= n; i += i & -i)
            for (int j = y; j <= m; j += j & -j)
                t[i][j] += v;
    }
    long long query(int x, int y) {                     // prefix sum of [1..x][1..y]
        long long s = 0;
        for (int i = x; i > 0; i -= i & -i)
            for (int j = y; j > 0; j -= j & -j)
                s += t[i][j];
        return s;
    }
    long long rect(int x1, int y1, int x2, int y2) {
        return query(x2, y2) - query(x1 - 1, y2) - query(x2, y1 - 1) + query(x1 - 1, y1 - 1);
    }
};

Memory — fine for , impossible for .

2D segment tree

A segment tree over rows, where each node holds a segment tree over columns.

BuildQueryUpdateMemory
2D BIT
2D segment tree
2D segment tree, sparse

No lazy propagation in 2D

Range updates on a 2D segment tree with lazy propagation do not work in general — a lazy tag on a row node cannot be pushed into the column trees consistently. Options: a 2D BIT with the four-BIT range-update trick (sums only), a sqrt decomposition, or offline processing.

When the grid is sparse

With up to but only points, an structure is impossible. The options:

1. Compress both coordinates. If all queries are known offline, map the distinct and values to and use a dense structure.

2. Merge sort tree / BIT of sorted vectors. Sort points by ; each BIT node stores the values of its range, sorted. Query with binary search inside each node. per query, memory, no updates.

3. Offline sweep + 1D BIT. Sort everything by and sweep, maintaining a 1D BIT over . This is almost always the right answer: time, memory, and it handles updates.

4. Persistent segment tree. Version contains all points with ; a rectangle query is the difference of two versions. Online, per query, memory.

5. KD-tree. per rectangle query, memory, and it supports insertions and nearest-neighbour queries.

The offline sweep — usually the answer

// count points inside each query rectangle
// decompose each rectangle into 2 prefix queries via inclusion-exclusion on x
sort(events.begin(), events.end());          // by x
BIT bit(maxY);
for (auto& e : events) {
    if (e.isPoint) bit.update(e.y, 1);
    else ans[e.qid] += e.sign * bit.query(e.y1, e.y2);
}

with memory — strictly better than a 2D structure on both counts whenever the queries can be sorted.

The decision table

Grid sizeUpdatesOnline?Structure
pointyes2D BIT
rectangle add, rectangle sumyesfour 2D BITs
large, sparsenonenooffline sweep + 1D BIT
large, sparsenoneyespersistent segment tree
large, sparsepointnooffline sweep (CDQ if 3 dimensions)
large, sparsepointyessparse 2D segment tree, or KD-tree
3+ dimensionsanynoCDQ divide and conquer

See also: Fenwick Tree · Merge Sort Tree · 2D Queries