Skip to content

Commit b9743e3

Browse files
committed
feat: Implement Offline Static Range Query data structure and add tests
1 parent db3fb80 commit b9743e3

File tree

3 files changed

+191
-0
lines changed

3 files changed

+191
-0
lines changed

test/staticrmq.test.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#define PROBLEM "https://judge.yosupo.jp/problem/staticrmq"
2+
3+
#include "../weilycoder/ds/offline_static_range_query.hpp"
4+
#include <cstdint>
5+
#include <iostream>
6+
#include <vector>
7+
using namespace std;
8+
using namespace weilycoder;
9+
10+
int main() {
11+
cin.tie(nullptr)->sync_with_stdio(false);
12+
cin.exceptions(cin.failbit | cin.badbit);
13+
size_t n, q;
14+
cin >> n >> q;
15+
16+
vector<uint32_t> datas(n);
17+
for (auto &x : datas)
18+
cin >> x;
19+
20+
OfflineStaticRangeQuery<NumberMinMonoid<uint32_t>> osrq(n);
21+
for (size_t i = 0; i < q; ++i) {
22+
size_t l, r;
23+
cin >> l >> r;
24+
osrq.add_query(l, r);
25+
}
26+
27+
for (const auto &res : osrq.process(datas))
28+
cout << res << '\n';
29+
return 0;
30+
}

weilycoder/ds/group.hpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
* @brief Group Definitions
77
*/
88

9+
#include <limits>
10+
911
namespace weilycoder {
1012
/**
1113
* @brief Additive Group
@@ -17,6 +19,36 @@ template <typename T> struct AddGroup {
1719
static constexpr T identity() { return T{}; }
1820
static constexpr T inverse(const T &a) { return -a; }
1921
};
22+
23+
/**
24+
* @brief Additive Monoid
25+
* @tparam T Type of the elements
26+
*/
27+
template <typename T> struct AddMonoid {
28+
using value_type = T;
29+
static constexpr T operation(const T &a, const T &b) { return a + b; }
30+
static constexpr T identity() { return T{}; }
31+
};
32+
33+
/**
34+
* @brief Minimum Monoid for numbers
35+
* @tparam T Type of the elements, must support std::numeric_limits
36+
*/
37+
template <typename T> struct NumberMinMonoid {
38+
using value_type = T;
39+
static constexpr T operation(const T &a, const T &b) { return a < b ? a : b; }
40+
static constexpr T identity() { return std::numeric_limits<T>::max(); }
41+
};
42+
43+
/**
44+
* @brief Maximum Monoid for numbers
45+
* @tparam T Type of the elements, must support std::numeric_limits
46+
*/
47+
template <typename T> struct NumberMaxMonoid {
48+
using value_type = T;
49+
static constexpr T operation(const T &a, const T &b) { return a > b ? a : b; }
50+
static constexpr T identity() { return std::numeric_limits<T>::min(); }
51+
};
2052
} // namespace weilycoder
2153

2254
#endif
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
#ifndef WEILYCODER_OFFLINE_STATIC_RANGE_QUERY_HPP
2+
#define WEILYCODER_OFFLINE_STATIC_RANGE_QUERY_HPP
3+
4+
#include "group.hpp"
5+
#include <cstddef>
6+
#include <numeric>
7+
#include <stdexcept>
8+
#include <utility>
9+
#include <vector>
10+
11+
namespace weilycoder {
12+
/**
13+
* @brief Offline Static Range Query Data Structure
14+
* @tparam Monoid The monoid defining the operation and identity
15+
* @tparam ptr_t The type used for indexing (default: size_t)
16+
*/
17+
template <typename Monoid, typename ptr_t = size_t> class OfflineStaticRangeQuery {
18+
public:
19+
using T = typename Monoid::value_type;
20+
21+
private:
22+
ptr_t query_count, data_size;
23+
24+
std::vector<std::vector<std::pair<ptr_t, ptr_t>>> queries;
25+
26+
std::vector<T> values;
27+
std::vector<ptr_t> parent;
28+
29+
/**
30+
* @brief Find with path compression and value aggregation
31+
* @param x The index to find
32+
* @return The root index after path compression
33+
*/
34+
ptr_t find(ptr_t x) {
35+
ptr_t y = parent[x];
36+
if (y == x)
37+
return x;
38+
parent[x] = find(y);
39+
if (y != parent[x])
40+
values[x] = Monoid::operation(values[x], values[y]);
41+
return parent[x];
42+
}
43+
44+
/**
45+
* @brief Initialize data values
46+
* @param datas The data values to initialize
47+
* @throws std::invalid_argument if the size does not match
48+
*/
49+
void init_datas(const std::vector<T> &datas) {
50+
values = datas;
51+
if (datas.size() != data_size)
52+
throw std::invalid_argument("Data size does not match the initialized size.");
53+
values.resize(datas.size() + 1, Monoid::identity());
54+
}
55+
56+
/**
57+
* @brief Initialize data values from iterators
58+
* @tparam InputIt The type of the input iterator
59+
* @param first The beginning iterator
60+
* @param last The ending iterator
61+
* @throws std::invalid_argument if the size does not match
62+
*/
63+
template <typename InputIt> void init_datas(InputIt first, InputIt last) {
64+
values.assign(first, last);
65+
if (values.size() != data_size)
66+
throw std::invalid_argument("Data size does not match the initialized size.");
67+
values.resize(data_size + 1, Monoid::identity());
68+
}
69+
70+
/**
71+
* @brief Process the queries and return results
72+
* @param datas The data values
73+
* @return A vector of results for each query
74+
*/
75+
std::vector<T> work(const std::vector<T> &datas) {
76+
parent.resize(datas.size() + 1);
77+
std::iota(parent.begin(), parent.end(), 0);
78+
79+
std::vector<T> results(query_count, Monoid::identity());
80+
for (size_t i = 1; i <= datas.size(); ++i) {
81+
parent[i - 1] = i;
82+
for (const auto &[l, idx] : queries[i])
83+
if (l < i)
84+
find(l), results[idx] = values[l];
85+
}
86+
return results;
87+
}
88+
89+
public:
90+
/**
91+
* @brief Constructor
92+
* @param n The size of the data
93+
*/
94+
OfflineStaticRangeQuery(ptr_t n) : query_count(0), data_size(n), queries(n + 1) {}
95+
96+
/**
97+
* @brief Add a query for the range [l, r)
98+
* @param l The left index (inclusive)
99+
* @param r The right index (exclusive)
100+
*/
101+
void add_query(ptr_t l, ptr_t r) {
102+
queries[std::min(r, data_size)].emplace_back(l, query_count++);
103+
}
104+
105+
/**
106+
* @brief Process the queries with the given data values
107+
* @param datas The data values
108+
* @return A vector of results for each query
109+
*/
110+
std::vector<T> process(const std::vector<T> &datas) {
111+
init_datas(datas);
112+
return work(datas);
113+
}
114+
115+
/**
116+
* @brief Process the queries with the given data values from iterators
117+
* @tparam InputIt The type of the input iterator
118+
* @param first The beginning iterator
119+
* @param last The ending iterator
120+
* @return A vector of results for each query
121+
*/
122+
template <typename InputIt> std::vector<T> process(InputIt first, InputIt last) {
123+
init_datas(first, last);
124+
return work(values);
125+
}
126+
};
127+
} // namespace weilycoder
128+
129+
#endif

0 commit comments

Comments
 (0)