cai_lw's competitive programming library
 
Loading...
Searching...
No Matches
batch_range_queries.hpp
1#include <algorithm>
2#include <cmath>
3#include <iterator>
4#include <numeric>
5#include <type_traits>
6#include <vector>
7
8namespace cplib {
9
10namespace impl {
11
12template <typename Range, typename Element>
13inline constexpr bool is_static_query_range_v = std::is_invocable_v<decltype(&Range::push_front), Range&, Element> &&
14 std::is_invocable_v<decltype(&Range::pop_front), Range&, Element> &&
15 std::is_invocable_v<decltype(&Range::push_back), Range&, Element> &&
16 std::is_invocable_v<decltype(&Range::pop_back), Range&, Element> &&
17 std::is_invocable_v<decltype(&Range::get), Range&>;
18
19}
20
36template <typename RandomIt, typename Range,
37 std::enable_if_t<impl::is_static_query_range_v<Range, typename std::iterator_traits<RandomIt>::value_type>>* =
38 nullptr>
39std::vector<decltype(std::declval<Range>().get())> batch_range_queries(
40 Range& range, const std::vector<std::pair<RandomIt, RandomIt>>& queries) {
41 if (queries.empty()) {
42 return {};
43 }
44 std::vector<size_t> order(queries.size());
45 std::iota(order.begin(), order.end(), 0);
46 std::sort(order.begin(), order.end(), [&](size_t i, size_t j) { return queries[i].first < queries[j].first; });
47 auto leftmost = std::min_element(queries.begin(), queries.end(), [](const auto& a, const auto& b) {
48 return a.first < b.first;
49 })->first;
50 auto rightmost = std::max_element(queries.begin(), queries.end(), [](const auto& a, const auto& b) {
51 return a.second < b.second;
52 })->second;
53 size_t block_size = std::max(size_t(1), size_t((rightmost - leftmost) / std::sqrt(queries.size())));
54 bool block_reverse = false;
55 auto block_sort_cmp = [&](size_t i, size_t j) {
56 return block_reverse ? queries[i].second > queries[j].second : queries[i].second < queries[j].second;
57 };
58 auto block_begin = order.begin();
59 for (auto block_end = block_begin; block_end < order.end(); block_end++) {
60 if (queries[*block_end].first - queries[*block_begin].first >= block_size) {
61 std::sort(block_begin, block_end, block_sort_cmp);
62 block_reverse ^= 1;
63 block_begin = block_end;
64 }
65 }
66 std::sort(block_begin, order.end(), block_sort_cmp);
67 RandomIt l = queries[order[0]].first, r = l;
68 std::vector<decltype(std::declval<Range>().get())> ans(queries.size());
69 for (auto i : order) {
70 auto [l_target, r_target] = queries[i];
71 while (l > l_target) {
72 range.push_front(*(--l));
73 }
74 while (r < r_target) {
75 range.push_back(*(r++));
76 }
77 while (l < l_target) {
78 range.pop_front(*(l++));
79 }
80 while (r > r_target) {
81 range.pop_back(*(--r));
82 }
83 ans[i] = range.get();
84 }
85 return ans;
86}
87
88} // namespace cplib
std::vector< decltype(std::declval< Range >().get())> batch_range_queries(Range &range, const std::vector< std::pair< RandomIt, RandomIt > > &queries)
Batch range queries using Mo's algorithm.
Definition: batch_range_queries.hpp:39