Balance two costs by choosing a threshold around . The array-splitting version is on Square Root Decomposition; this page covers the other sqrt tricks — the ones that split operations, values or time rather than indices.
1. Sqrt rebuilding — the general escape hatch
Keep a fast static structure plus a small buffer of recent updates.
query = static_answer + correction_from_buffer O(fast + |buffer|)
update = append to the buffer O(1)
every sqrt(q) updates: rebuild the static structure O(build)
Amortized per operation. This makes any static structure updatable:
| Static structure | Becomes |
|---|---|
| Sorted array | insertable “sorted list” |
| Convex hull | dynamic hull |
| Precomputed table | updatable table |
| Sparse table | supports point updates |
| Aho-Corasick automaton | supports pattern insertion |
The Aho-Corasick case is a nice one: rebuilding the automaton every insertions, and brute-forcing the buffered patterns, gives an “add patterns online” multi-pattern matcher.
2. Heavy/light splitting by frequency
Split objects by a threshold and handle each class differently.
| Setting | Heavy | Light |
|---|---|---|
| Values with count | at most of them — precompute per heavy value | few occurrences — brute force |
| Vertices with degree | at most of them | iterate their neighbours directly |
| Patterns with length | at most of them | group by length |
| Queries with range | few of them | brute force |
Triangle counting is the classic: orient each edge from lower to higher degree; every vertex then has out-degree , so enumerating pairs of out-neighbours is .
// count triangles in O(m^1.5)
for (auto [u, v] : edges)
if (make_pair(deg[u], u) < make_pair(deg[v], v)) g[u].push_back(v);
else g[v].push_back(u);
for (int u = 0; u < n; u++) {
for (int v : g[u]) mark[v] = 1;
for (int v : g[u]) for (int w : g[v]) if (mark[w]) triangles++;
for (int v : g[u]) mark[v] = 0;
}3. Sqrt on values
When the values are bounded by , split at :
- small values — maintain a frequency array;
- large values — there are at most distinct ones with high multiplicity; handle them individually.
Used for “count subsets summing to ” and knapsack variants where the number of distinct item weights is — a genuinely useful observation, since distinct positive weights summing to force .
4. Sqrt on time (operation batching)
Process queries in blocks of :
- within a block, treat the structure as static and answer with brute force over the block’s updates;
- at the end of each block, apply all its updates in bulk.
This is the shape of Mo’s algorithm and of many “rebuild periodically” solutions.
Choosing the threshold
is the right answer only when the two sides have equal constants. When updates are much rarer than queries (or vice versa), tune — a 2-3× speedup is typical, and it is often the difference between TLE and AC.
Measure rather than guess: try .
When sqrt techniques are right
per operation with is about — comfortable. At it is and too slow.
Rule of thumb: sqrt methods are for . Their value is that they work when nothing else does — for operations that no tree structure can merge, and for problems whose structure resists -factor solutions.
See also: Square Root Decomposition · Mo’s Algorithm · Offline Query Processing