Split the array into blocks of size . Queries touch whole blocks plus individual elements, giving per operation. Slower than a segment tree asymptotically, but it handles operations a segment tree cannot.

Basic range sum

int B;                                   // block size ~ sqrt(n)
vector<long long> a, blockSum;
 
void build(int n) {
    B = max(1, (int)sqrt(n));
    blockSum.assign((n + B - 1) / B, 0);
    for (int i = 0; i < n; i++) blockSum[i / B] += a[i];
}
 
void update(int i, long long v) { blockSum[i / B] += v - a[i]; a[i] = v; }
 
long long query(int l, int r) {
    long long res = 0;
    while (l <= r && l % B) res += a[l++];               // partial left block
    while (l + B - 1 <= r) { res += blockSum[l / B]; l += B; }   // whole blocks
    while (l <= r) res += a[l++];                        // partial right block
    return res;
}

Choosing the block size

balances “number of blocks” against “elements per block”. But when updates and queries have different costs, tune it:

For updates and queries on , is right. If updates are rare, use larger blocks.

Why bother, given segment trees exist

Sqrt decomposition wins in three situations:

1. The operation has no efficient merge. A segment tree needs an associative combine. Sqrt decomposition only needs “rebuild a block in ”, which is a far weaker requirement.

2. You need per-block auxiliary structures. Keeping each block sorted allows queries a segment tree cannot answer cheaply:

// count elements > x in [l, r]
long long count = 0;
// partial blocks: scan directly
// whole blocks: binary search in the sorted copy
count += sorted[b].end() - upper_bound(sorted[b].begin(), sorted[b].end(), x);

per query, and it supports “range add” by keeping a per-block offset.

3. The problem is genuinely offline — see Mo’s algorithm, which is sqrt decomposition applied to the query order rather than the array.

The variants

VariantIdeaComplexity
Blocks over the arraythe standard
Blocks + sorted copiesorder-statistics queries
Blocks + lazy per-block tagrange assign / add
Mo’s algorithmsort the queries by block
Mo on treesMo over the Euler tour
Sqrt rebuildingrebuild the structure every operations amortized
Sqrt on valuessplit the value range, not the indicesproblem-specific
Buffered updatescollect updates, then rebuild

Sqrt rebuilding

A general escape hatch: keep a fast static structure plus a small buffer of recent updates. Answer a query as “static answer, corrected by the buffered updates”, and rebuild the static structure every updates. This turns many “impossible to update” structures into updatable ones at a cost — it works for convex hulls, sorted lists, and precomputed tables alike.

Sqrt decomposition on trees

Split the tree into connected blocks of size , or by depth into layers. Less common than HLD or centroid decomposition, but occasionally the only thing that fits an unusual query.

When is fast enough

With , — comfortable. At it is and too slow. The rule of thumb: sqrt techniques are for .

See also: Mo’s Algorithm · Segment Tree · Range Query Techniques