-
-
Notifications
You must be signed in to change notification settings - Fork 7.3k
/
sparse_table.cpp
165 lines (144 loc) · 5.93 KB
/
sparse_table.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/**
* @file
* @brief Implementation of [Sparse
* Table](https://brilliant.org/wiki/sparse-table/) for `min()` function.
* @author [Mann Patel](https://github.com/manncodes)
* @details
* Sparse Table is a data structure, that allows answering range queries.
* It can answer most range queries in O(logn), but its true power is answering
* range minimum queries (or equivalent range maximum queries). For those
* queries it can compute the answer in O(1) time. The only drawback of this
* data structure is, that it can only be used on immutable arrays. This means,
* that the array cannot be changed between two queries.
*
* If any element in the array changes, the complete data structure has to be
* recomputed.
*
* @todo make stress tests.
*
* @warning
* This sparse table is made for `min(a1,a2,...an)` duplicate invariant
* function. This implementation can be changed to other functions like
* `gcd()`, `lcm()`, and `max()` by changing a few lines of code.
*/
#include <array> /// for std::array
#include <cassert> /// for assert
#include <cstdint>
#include <iostream> /// for IO operations
/**
* @namespace data_structures
* @brief Data Structures algorithms
*/
namespace data_structures {
/**
* @namespace sparse_table
* @brief Functions for Implementation of [Sparse
* Table](https://brilliant.org/wiki/sparse-table/)
*/
namespace sparse_table {
/**
* @brief A struct to represent sparse table for `min()` as their invariant
* function, for the given array `A`. The answer to queries are stored in the
* array ST.
*/
constexpr uint32_t N = 12345; ///< the maximum size of the array.
constexpr uint8_t M = 14; ///< ceil(log2(N)).
struct Sparse_table {
size_t n = 0; ///< size of input array.
/** @warning check if `N` is not less than `n`. if so, manually increase the
* value of N */
std::array<int64_t, N> A = {}; ///< input array to perform RMQ.
std::array<std::array<int64_t, N>, M>
ST{}; ///< the sparse table storing `min()` values for given interval.
std::array<int64_t, N> LOG = {}; ///< where floor(log2(i)) are precomputed.
/**
* @brief Builds the sparse table for computing min/max/gcd/lcm/...etc
* for any contiguous sub-segment of the array.This is an example of
* computing the index of the minimum value.
* @return void
* @complexity: O(n.log(n))
*/
void buildST() {
LOG[0] = -1;
for (size_t i = 0; i < n; ++i) {
ST[0][i] = static_cast<int64_t>(i);
LOG[i + 1] = LOG[i] + !(i & (i + 1)); ///< precomputing `log2(i+1)`
}
for (size_t j = 1; static_cast<size_t>(1 << j) <= n; ++j) {
for (size_t i = 0; static_cast<size_t>(i + (1 << j)) <= n; ++i) {
/**
* @note notice how we deal with the range of length `pow(2,i)`,
* and we can reuse the computation that we did for the range of
* length `pow(2,i-1)`.
*
* So, ST[j][i] = min( ST[j-1][i], ST[j-1][i + pow(2,j-1)]).
* @example ST[2][3] = min(ST[1][3], ST[1][5])
*/
int64_t x = ST[j - 1][i]; ///< represents minimum value over
///< the range [j,i]
int64_t y =
ST[j - 1]
[i + (1 << (j - 1))]; ///< represents minimum value over
///< the range [j,i + pow(2,j-1)]
ST[j][i] =
(A[x] <= A[y] ? x : y); ///< represents minimum value over
///< the range [j,i]
}
}
}
/**
* @brief Queries the sparse table for the value of the interval [l, r]
* (i.e. from l to r inclusive).
* @param l the left index of the range (inclusive).
* @param r the right index of the range (inclusive).
* @return the computed value of the given interval.
* @complexity: O(1)
*/
int64_t query(int64_t l, int64_t r) {
int64_t g = LOG[r - l + 1]; ///< smallest power of 2 covering [l,r]
int64_t x = ST[g][l]; ///< represents minimum value over the range
///< [g,l]
int64_t y =
ST[g][r - (1 << g) + 1]; ///< represents minimum value over the
///< range [g, r - pow(2,g) + 1]
return (A[x] <= A[y] ? x : y); ///< represents minimum value over
///< the whole range [l,r]
}
};
} // namespace sparse_table
} // namespace data_structures
/**
* @brief Self-test implementations
* @returns void
*/
static void test() {
/* We take an array as an input on which we need to perform the ranged
* minimum queries[RMQ](https://en.wikipedia.org/wiki/Range_minimum_query).
*/
std::array<int64_t, 10> testcase = {
1, 2, 3, 4, 5,
6, 7, 8, 9, 10}; ///< array on which RMQ will be performed.
size_t testcase_size =
sizeof(testcase) / sizeof(testcase[0]); ///< size of self test's array
data_structures::sparse_table::Sparse_table
st{}; ///< declaring sparse tree
std::copy(std::begin(testcase), std::end(testcase),
std::begin(st.A)); ///< copying array to the struct
st.n = testcase_size; ///< passing the array's size to the struct
st.buildST(); ///< precomputing sparse tree
// pass queries of the form: [l,r]
assert(st.query(1, 9) == 1); ///< as 1 is smallest from 1..9
assert(st.query(2, 6) == 2); ///< as 2 is smallest from 2..6
assert(st.query(3, 8) == 3); ///< as 3 is smallest from 3..8
std::cout << "Self-test implementations passed!" << std::endl;
}
/**
* @brief Main function
* @param argc commandline argument count (ignored)
* @param argv commandline array of arguments (ignored)
* @returns 0 on exit
*/
int main(int argc, char *argv[]) {
test(); // run self-test implementations
return 0;
}