TL; DR
Build a sparse table over blocks of size . Now we only need to answer queries of size smaller than . For that, simulate a minqueue of size over the array, and store a mask of the elements that are currently active in the minqueue. Let be the minqueue mask when the simulation is at position . Now we can see that, if , then query(l, r) = r - most_significant_set_bit(mask[r] & ((1<<(r-l+1))-1)).
template<typename T> struct rmq {
vector<T> v;
int n; static const int b = 30;
vector<int> mask, t;
int op(int x, int y) { return v[x] < v[y] ? x : y; }
int msb(int x) { return __builtin_clz(1)-__builtin_clz(x); }
int small(int r, int sz = b) { return r-msb(mask[r]&((1<<sz)-1)); }
rmq(const vector<T>& v_) : v(v_), n(v.size()), mask(n), t(n) {
for (int i = 0, at = 0; i < n; mask[i++] = at |= 1) {
at = (at<<1)&((1<<b)-1);
while (at and op(i, i-msb(at&-at)) == i) at ^= at&-at;
}
for (int i = 0; i < n/b; i++) t[i] = small(b*i+b-1);
for (int j = 1; (1<<j) <= n/b; j++) for (int i = 0; i+(1<<j) <= n/b; i++)
t[n/b*j+i] = op(t[n/b*(j-1)+i], t[n/b*(j-1)+i+(1<<(j-1))]);
}
T query(int l, int r) {
if (r-l+1 <= b) return v[small(r, r-l+1)];
int ans = op(small(l+b-1), small(r));
int x = l/b+1, y = r/b-1;
if (x <= y) {
int j = msb(y-x+1);
ans = op(ans, op(t[n/b*j+x], t[n/b*j+y-(1<<j)+1]));
}
return v[ans];
}
};
Hello, Codeforces!
Here I’ll share an algorithm to solve the classic problem of Range Minimum Query (RMQ): given a static array (there won’t be any updates), we want to find, for every , the index of the minimum value of the sub-array of that starts at index and ends at index . That is, we want to find . If there are more than one such indices, we can answer any of them.
I would like to thank tfg for showing me this algorithm. If you have read about it somewhere, please share the source. The only source I could find was a comment from jcg, where he explained it briefly.
Introduction
Sparse table is a well known data structure to query for the minimum over a range in constant time. However, it requires construction time and memory. Interestingly, we can use a sparse table to help us answer RMQ with linear time construction: even though we can’t build a sparse table over all the elements of the array, we can build a sparse table over fewer elements.
To do that, let us divide the array into blocks of size and compute the minimum of each block. If we then build a sparse table over these minimums, it will cost . Finally, if we choose , we get time and space for construction of the sparse table!
So, if our query indices happen to align with the limits of the blocks, we can find the answer. But we might run into the following cases:
- Query range is too small, so it fits entirely inside one block:

- Query range is large and doesn’t align with block limits:

Note that, on the second case, we can use our sparse table to query the middle part (in gray). In both cases, if were able to make small queries (queries such that ), we would be done.
Handling small queries
Let’s consider queries ending at the same position . Take the following array and .

Obviously, . Since , we can think of the position as “important”. Position , though, is not important, because . Basically, for fixed , a position is important if the value at that position is smaller than all the values to the right of it. In this example, the important positions are . In the following image, important elements are represented with and others with .

Since we only have to answer queries with size at most , we can store this information in a mask of size : in this example , assuming . If we had these masks for the whole array, how could we figure out the minimum over a range? Well, we can simply take , look at it’s least significant bits, and out of those bits take most significant one! The index of that bit would tell us how far away from the answer is.
Using our previous example, if the query was from to , we would take , only look at the least significant bits (that would give us ) and out of that take the index of the most significant set bit: . So the minimum is at position .
Now we only need to figure out how to compute theses masks. If we have some mask representing position , lets change it to represent position . Obviously, a position that was not important can’t become important, so we won’t need to turn on any bits. However, some positions that were important can stop being important. To handle that, we can just keep turning off the least significant currently set bit of our mask, until there are no more bits to turn off or the value at is greater than the element at the position represented by the least significant set bit of the mask (in that case we can stop, because the elements represented by important positions to the left of the least significant set bit are even smaller).
Let’s append an element with value at the end of array and update our mask.

Since , we turn off that bit. After that, once again , so we also turn off that bit. Now we have that , so we stop turning off bits. Finally, we need to append a 1 to the right of the mask, so it becomes (assuming ).

This process takes time: only one bit is turned on for each position of the array, so the total number of times we turn a bit off at most , and using bit operations we can get and turn off the least significant currently set bit in .
Implementation
Here is a detailed C++ implementation of the whole thing.
template<typename T> struct rmq {
vector<T> v; int n;
static const int b = 30; // block size
vector<int> mask, t; // mask and sparse table
int op(int x, int y) {
return v[x] < v[y] ? x : y;
}
// least significant set bit
int lsb(int x) {
return x & -x;
}
// index of the most significant set bit
int msb_index(int x) {
return __builtin_clz(1)-__builtin_clz(x);
}
// answer query of v[r-size+1..r] using the masks, given size <= b
int small(int r, int size = b) {
// get only 'size' least significant bits of the mask
// and then get the index of the msb of that
int dist_from_r = msb_index(mask[r] & ((1<<size)-1));
return r - dist_from_r;
}
rmq(const vector<T>& v_) : v(v_), n(v.size()), mask(n), t(n) {
int curr_mask = 0;
for (int i = 0; i < n; i++) {
// shift mask by 1, keeping only the 'b' least significant bits
curr_mask = (curr_mask<<1) & ((1<<b)-1);
while (curr_mask > 0 and op(i, i - msb_index(lsb(curr_mask))) == i) {
// current value is smaller than the value represented by the
// last 1 in curr_mask, so we need to turn off that bit
curr_mask ^= lsb(curr_mask);
}
// append extra 1 to the mask
curr_mask |= 1;
mask[i] = curr_mask;
}
// build sparse table over the n/b blocks
// the sparse table is linearized, so what would be at
// table[j][i] is stored in table[(n/b)*j + i]
for (int i = 0; i < n/b; i++) t[i] = small(b*i+b-1);
for (int j = 1; (1<<j) <= n/b; j++) for (int i = 0; i+(1<<j) <= n/b; i++)
t[n/b*j+i] = op(t[n/b*(j-1)+i], t[n/b*(j-1)+i+(1<<(j-1))]);
}
// query(l, r) returns the actual minimum of v[l..r]
// to get the index, just change the first and last lines of the function
T query(int l, int r) {
// query too small
if (r-l+1 <= b) return v[small(r, r-l+1)];
// get the minimum of the endpoints
// (there is no problem if the ranges overlap with the sparse table query)
int ans = op(small(l+b-1), small(r));
// 'x' and 'y' are the blocks we need to query over
int x = l/b+1, y = r/b-1;
if (x <= y) {
int j = msb_index(y-x+1);
ans = op(ans, op(t[n/b*j+x], t[n/b*j+y-(1<<j)+1]));
}
return v[ans];
}
};
But is it fast?
As you might have guessed, although the asymptotic complexity is optimal, the constant factor of this algorithm is not so small. To get a better understanding of how fast it actually is and how it compares with other data structures capable of answering RMQ, I did some benchmarks (link to the benchmark files).
I compared the following data structures. Complexities written in the notation means that the data structure requires construction time and query time.
- RMQ 1 (implementation of RMQ described in this post): ;
- RMQ 2 (different algorithm, implementation by catlak_profesor_mfb, from this blog): ;
- Sparse Table: ;
- Sqrt-tree (tutorial and implementation from this blog by gepardo): ;
- Standard Segment Tree (recursive implementation): ;
- Iterative (non recursive) Segment Tree: .
The data structures were executed with array size , , . At each one of these array sizes, build time and the time to answer queries were measured, averaging across 10 runs. The codes were compiled with flag. Below are the results on my machine.


Results in table form
Build time (ms):
| SIZE (x 10^6) | RMQ 1 | RMQ 2 | SPARSE TABLE | SQRT TREE | SEG ITERATIVE | SEG RECURSIVE |
|---|---|---|---|---|---|---|
| 1 | 15 | 27 | 42 | 43 | 3 | 7 |
| 2 | 29 | 65 | 90 | 83 | 7 | 15 |
| 3 | 42 | 107 | 141 | 134 | 11 | 22 |
| 4 | 56 | 141 | 188 | 168 | 15 | 30 |
| 5 | 70 | 195 | 244 | 235 | 19 | 39 |
| 6 | 84 | 230 | 294 | 267 | 23 | 46 |
| 7 | 98 | 263 | 345 | 302 | 27 | 53 |
| 8 | 112 | 297 | 397 | 332 | 31 | 59 |
| 9 | 124 | 348 | 459 | 449 | 35 | 69 |
| 10 | 140 | 382 | 515 | 484 | 39 | 77 |
Query time for queries (ms):
| SIZE (x 10^6) | RMQ 1 | RMQ 2 | SPARSE TABLE | SQRT TREE | SEG ITERATIVE | SEG RECURSIVE |
|---|---|---|---|---|---|---|
| 1 | 44 | 104 | 18 | 51 | 138 | 323 |
| 2 | 66 | 146 | 22 | 67 | 210 | 462 |
| 3 | 77 | 169 | 24 | 79 | 238 | 527 |
| 4 | 87 | 183 | 24 | 86 | 257 | 563 |
| 5 | 97 | 201 | 25 | 84 | 264 | 586 |
| 6 | 96 | 211 | 26 | 90 | 273 | 605 |
| 7 | 102 | 222 | 27 | 95 | 283 | 615 |
| 8 | 107 | 228 | 27 | 94 | 292 | 629 |
| 9 | 110 | 237 | 28 | 105 | 295 | 647 |
| 10 | 113 | 240 | 28 | 107 | 303 | 658 |
Conclusion
We have an algorithm with optimal complexity to answer RMQ, and it is also simple to understand and implement. With the benchmark made, we can see that its construction is much faster than Sparse Table and Sqrt-tree, but a little slower than Segment Trees. Its query time seems to be roughly the same as Sqrt-tree, losing only to Sparse Table, which have shown to be the fastest in query time.