競技プログラミング用 C++ ライブラリ
data_structure/offline_range_kth_smallest.hpp · GitHub で見る
共通ページ
#include "lib/data_structure/offline_range_kth_smallest.hpp"
std::vector<int> a{3, 1, 4, 1, 5};
std::vector<std::array<int, 3>> queries{
{0, 5, 2},
{1, 4, 1},
{2, 5, 0}
};
auto answer = offline_range_kth_smallest(a, queries); // {3, 1, 1}
| 操作 | 内容・返り値 |
|---|---|
offline_range_kth_smallest(values,queries) |
各 {l,r,k} について、values[l,r) の小さい方から 0 始まりで k 番目の値を、問い合わせと同じ順の vector<T> で返す |
values は vector<T>、queries は span<const array<int,3>> です。vector<array<int,3>> をそのまま渡せます。元の列と問い合わせは変更しません。
列長を \(N\) として、各問い合わせは \(0\le l<r\le N\)、\(0\le k<r-l\) を満たすものとします。列長・問い合わせ数は INT_MAX 以下です。空の問い合わせ列には空の vector を返します。
T はコピーと大小・等値比較が可能な型とします。整数・文字列などに対応します。問い合わせを一件ずつ処理する場合は Wavelet Matrix を使えます。
列長を \(N\)、問い合わせ数を \(Q\)、異なる値の数を \(\sigma\) とし、比較・コピーを定数時間とします。64 bit 以下の整数列では \(O((N+Q)\log\sigma)\) 時間、それ以外の型では \(O(N\log N+Q\log\sigma)\) 時間、追加空間は \(O(N+Q)\) です。値が一種類以下の場合は \(O(N+Q)\) 時間です。
#ifndef OFFLINE_RANGE_KTH_SMALLEST_HPP
#define OFFLINE_RANGE_KTH_SMALLEST_HPP
#include "../components/offline_range_kth.hpp"
#include "../components/wavelet_compression.hpp"
#include <array>
#include <cassert>
#include <limits>
#include <span>
#include <vector>
template <class T>
std::vector<T> offline_range_kth_smallest(const std::vector<T> &values, std::span<const std::array<int, 3>> queries) {
assert(values.size() <= std::size_t(std::numeric_limits<int>::max()) && queries.size() <= std::size_t(std::numeric_limits<int>::max()));
if (queries.empty()) return {};
offline_range_kth_detail::Frontier frontier(queries.size());
for (std::size_t i = 0; i < queries.size(); ++i) {
auto [l, r, k] = queries[i];
assert(0 <= l && l < r && std::size_t(r) <= values.size() && 0 <= k && k < r - l);
frontier.left[i] = unsigned(l); frontier.right[i] = unsigned(r); frontier.rank[i] = unsigned(k);
}
std::vector<T> sorted;
std::vector<int> positions, offsets;
int ordered = 0;
auto codes = wavelet_detail::compress<T, false>(values, sorted, ordered, positions, offsets);
unsigned bits = std::bit_width(unsigned(sorted.size() - 1));
if (bits && !ordered) offline_range_kth_detail::run(frontier, std::move(codes), bits);
std::vector<T> result;
result.reserve(queries.size());
for (std::size_t i = 0; i < queries.size(); ++i) {
if (ordered) {
auto [l, r, k] = queries[i];
result.push_back(values[ordered > 0 ? l + k : r - 1 - k]);
} else result.push_back(sorted[frontier.answer[i]]);
}
return result;
}
#endif
#line 1 "data_structure/offline_range_kth_smallest.hpp"
#line 1 "components/offline_range_kth.hpp"
#include <array>
#include <bit>
#include <cstddef>
#include <cstdint>
#include <utility>
#include <vector>
#if defined(__x86_64__) || defined(__i386__)
#include <immintrin.h>
#endif
namespace offline_range_kth_detail {
struct Frontier {
std::vector<std::uint32_t> left, right, rank, answer;
explicit Frontier(std::size_t n) : left(n), right(n), rank(n), answer(n) {}
};
inline void advance(Frontier &queries, const std::vector<std::uint32_t> ¤t, const std::vector<std::uint32_t> &prefix,
unsigned zeros, bool finish, std::size_t start = 0) {
for (std::size_t i = start; i < queries.left.size(); ++i) {
auto left = queries.left[i], right = queries.right[i];
auto a = prefix[left], b = prefix[right], count = right - left - b + a;
bool one = queries.rank[i] >= count;
if (finish) queries.answer[i] = (current[left] & ~1U) | unsigned(one);
else {
queries.left[i] = one ? zeros + a : left - a;
queries.right[i] = one ? zeros + b : right - b;
queries.rank[i] -= one ? count : 0;
}
}
}
inline void run_scalar(Frontier &queries, std::vector<std::uint32_t> current, unsigned bits) {
auto n = current.size();
std::vector<std::uint32_t> next(n), prefix(n + 1);
while (bits--) {
for (std::size_t i = 0; i < n; ++i) prefix[i + 1] = prefix[i] + ((current[i] >> bits) & 1);
unsigned zeros = unsigned(n) - prefix[n];
if (bits) {
std::array<unsigned, 2> cursor{0, zeros};
for (auto value : current) next[cursor[(value >> bits) & 1]++] = value;
}
advance(queries, current, prefix, zeros, bits == 0);
if (bits) current.swap(next);
}
}
#if defined(__x86_64__) || defined(__i386__)
struct Tables {
std::array<unsigned char, 8> prefix{};
std::array<std::uint32_t, 2> packed{};
std::array<int, 8> permutation{};
};
inline constexpr auto tables = [] {
std::array<Tables, 256> result{};
for (unsigned mask = 0; mask < 256; ++mask) {
unsigned count = 0, selected = 0;
for (unsigned i = 0; i < 8; ++i) {
if (!(i & 3)) result[mask].packed[i >> 2] = count << 8;
else result[mask].packed[i >> 2] |= (count - (result[mask].packed[i >> 2] >> 8)) << (2 * (i & 3));
if ((mask >> i) & 1) result[mask].permutation[selected++] = int(i);
count += (mask >> i) & 1;
result[mask].prefix[i] = static_cast<unsigned char>(count);
}
}
return result;
}();
inline bool packed_ranks(const Frontier &queries, std::size_t n) {
if (n < 32768 || n > 0x00ffffff || queries.left.size() < 4096) return false;
unsigned shift = std::bit_width(n) - 6;
std::size_t stride = queries.left.size() / 32;
std::uint64_t occupied = 0;
for (std::size_t i = 0; i < 32; ++i) {
occupied |= std::uint64_t(1) << (queries.left[i * stride] >> shift);
occupied |= std::uint64_t(1) << (queries.right[i * stride] >> shift);
}
return std::popcount(occupied) >= 12;
}
__attribute__((target("avx2"), always_inline))
inline void store_selected(std::uint32_t *output, unsigned remaining, __m256i values, unsigned mask) {
auto permutation = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(tables[mask].permutation.data()));
auto selected = _mm256_permutevar8x32_epi32(values, permutation);
if (remaining >= 8) _mm256_storeu_si256(reinterpret_cast<__m256i *>(output), selected);
else {
auto active = _mm256_cmpgt_epi32(_mm256_set1_epi32(std::popcount(mask)), _mm256_setr_epi32(0, 1, 2, 3, 4, 5, 6, 7));
_mm256_maskstore_epi32(reinterpret_cast<int *>(output), active, selected);
}
}
template <bool Packed, bool Finish>
__attribute__((target("avx2")))
void route(Frontier &queries, const std::vector<std::uint32_t> ¤t, const std::vector<std::uint32_t> &prefix, unsigned zeros) {
std::size_t i = 0;
auto middle = _mm256_set1_epi32(int(zeros));
for (; i + 8 <= queries.left.size(); i += 8) {
auto left = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(queries.left.data() + i));
auto right = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(queries.right.data() + i));
auto rank = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(queries.rank.data() + i));
auto a = _mm256_i32gather_epi32(reinterpret_cast<const int *>(prefix.data()), Packed ? _mm256_srli_epi32(left, 2) : left, 4);
auto b = _mm256_i32gather_epi32(reinterpret_cast<const int *>(prefix.data()), Packed ? _mm256_srli_epi32(right, 2) : right, 4);
if constexpr (Packed) {
auto mask = _mm256_set1_epi32(3);
auto sa = _mm256_slli_epi32(_mm256_and_si256(left, mask), 1), sb = _mm256_slli_epi32(_mm256_and_si256(right, mask), 1);
a = _mm256_add_epi32(_mm256_srli_epi32(a, 8), _mm256_and_si256(_mm256_srlv_epi32(a, sa), mask));
b = _mm256_add_epi32(_mm256_srli_epi32(b, 8), _mm256_and_si256(_mm256_srlv_epi32(b, sb), mask));
}
auto lzero = _mm256_sub_epi32(left, a), rzero = _mm256_sub_epi32(right, b);
auto count = _mm256_sub_epi32(rzero, lzero), take_zero = _mm256_cmpgt_epi32(count, rank);
if constexpr (Finish) {
auto value = _mm256_i32gather_epi32(reinterpret_cast<const int *>(current.data()), left, 4);
value = _mm256_or_si256(_mm256_and_si256(value, _mm256_set1_epi32(-2)), _mm256_andnot_si256(take_zero, _mm256_set1_epi32(1)));
_mm256_storeu_si256(reinterpret_cast<__m256i *>(queries.answer.data() + i), value);
} else {
_mm256_storeu_si256(reinterpret_cast<__m256i *>(queries.left.data() + i), _mm256_blendv_epi8(_mm256_add_epi32(middle, a), lzero, take_zero));
_mm256_storeu_si256(reinterpret_cast<__m256i *>(queries.right.data() + i), _mm256_blendv_epi8(_mm256_add_epi32(middle, b), rzero, take_zero));
_mm256_storeu_si256(reinterpret_cast<__m256i *>(queries.rank.data() + i), _mm256_sub_epi32(rank, _mm256_andnot_si256(take_zero, count)));
}
}
for (; i < queries.left.size(); ++i) {
auto left = queries.left[i], right = queries.right[i];
auto a = prefix[Packed ? left >> 2 : left], b = prefix[Packed ? right >> 2 : right];
if constexpr (Packed) {
a = (a >> 8) + ((a >> (2 * (left & 3))) & 3);
b = (b >> 8) + ((b >> (2 * (right & 3))) & 3);
}
auto count = right - left - b + a;
bool one = queries.rank[i] >= count;
if constexpr (Finish) queries.answer[i] = (current[left] & ~1U) | unsigned(one);
else {
queries.left[i] = one ? zeros + a : left - a;
queries.right[i] = one ? zeros + b : right - b;
queries.rank[i] -= one ? count : 0;
}
}
}
template <bool Packed>
__attribute__((target("avx2")))
void run_avx2(Frontier &queries, std::vector<std::uint32_t> current, unsigned bits) {
std::size_t n = current.size();
current.resize(n + 8);
std::vector<std::uint32_t> next(n + 8), prefix(Packed ? (n + 3) / 4 + 1 : n + 1);
std::vector<unsigned char> masks((n + 7) / 8);
while (bits--) {
unsigned ones = 0;
std::size_t i = 0;
for (; i + 8 <= n; i += 8) {
auto value = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(current.data() + i));
auto selected = _mm256_sll_epi32(value, _mm_cvtsi32_si128(31 - int(bits)));
unsigned mask = unsigned(_mm256_movemask_ps(_mm256_castsi256_ps(selected)));
masks[i / 8] = static_cast<unsigned char>(mask);
if constexpr (Packed) {
prefix[i / 4] = (ones << 8) + tables[mask].packed[0];
prefix[i / 4 + 1] = (ones << 8) + tables[mask].packed[1];
} else {
auto local = _mm256_cvtepu8_epi32(_mm_loadl_epi64(reinterpret_cast<const __m128i *>(tables[mask].prefix.data())));
_mm256_storeu_si256(reinterpret_cast<__m256i *>(prefix.data() + i + 1), _mm256_add_epi32(local, _mm256_set1_epi32(int(ones))));
}
ones += unsigned(std::popcount(mask));
}
for (; i <= n; ++i) {
if constexpr (Packed) {
if (!(i & 3)) prefix[i / 4] = ones << 8;
else prefix[i / 4] |= (ones - (prefix[i / 4] >> 8)) << (2 * (i & 3));
} else prefix[i] = ones;
if (i < n) ones += (current[i] >> bits) & 1;
}
unsigned zeros = unsigned(n) - ones;
if (bits) {
unsigned zero = 0, one = zeros;
i = 0;
for (; i + 8 <= n; i += 8) {
auto value = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(current.data() + i));
unsigned mask = masks[i / 8], count = unsigned(std::popcount(mask));
store_selected(next.data() + zero, zeros - zero, value, mask ^ 255);
store_selected(next.data() + one, unsigned(n + 8) - one, value, mask);
zero += 8 - count; one += count;
}
for (; i < n; ++i) {
auto value = current[i];
if ((value >> bits) & 1) next[one++] = value;
else next[zero++] = value;
}
route<Packed, false>(queries, current, prefix, zeros);
current.swap(next);
} else route<Packed, true>(queries, current, prefix, zeros);
}
}
#endif
inline void run(Frontier &queries, std::vector<std::uint32_t> current, unsigned bits) {
#if defined(__x86_64__) || defined(__i386__)
if (__builtin_cpu_supports("avx2")) {
if (packed_ranks(queries, current.size())) run_avx2<true>(queries, std::move(current), bits);
else run_avx2<false>(queries, std::move(current), bits);
return;
}
#endif
run_scalar(queries, std::move(current), bits);
}
}
#line 1 "components/wavelet_compression.hpp"
#include <algorithm>
#line 7 "components/wavelet_compression.hpp"
#include <cassert>
#line 9 "components/wavelet_compression.hpp"
#include <limits>
#include <type_traits>
#line 12 "components/wavelet_compression.hpp"
namespace wavelet_detail {
template<class T>
std::uint32_t lower_code(const std::vector<T> &values, bool dense, const T &x) {
if (values.empty()) return 0;
if constexpr (std::is_integral_v<T> && sizeof(T) <= 8)
if (dense) {
if (x <= values.front()) return 0;
if (values.back() < x) return static_cast<int>(values.size());
return std::uint64_t(x) - std::uint64_t(values.front());
}
int p = 0, length = static_cast<int>(values.size());
while (length > 1) {
int half = length / 2;
p += (values[p + half] < x) * half;
length -= half;
}
return p + (values[p] < x);
}
template<class T, bool WithPositions = true>
std::vector<std::uint32_t> compress(const std::vector<T> &a, std::vector<T> &values,
int &ordered, std::vector<int> &positions, std::vector<int> &offsets) {
assert(a.size() <= std::size_t(std::numeric_limits<int>::max()));
const int n = static_cast<int>(a.size());
ordered = 0;
if constexpr (WithPositions) offsets.clear();
std::vector<std::uint32_t> current(n);
bool ascending = std::is_sorted(a.begin(), a.end());
if (ascending || std::is_sorted(a.rbegin(), a.rend())) {
ordered = ascending ? 1 : -1;
values.clear();
values.reserve(n);
for (int i = 0; i < n; ++i) {
int j = ascending ? i : n - 1 - i;
if (values.empty() || values.back() != a[j]) values.push_back(a[j]);
current[j] = static_cast<int>(values.size()) - 1;
}
if (values.capacity() > 2 * values.size()) values.shrink_to_fit();
if constexpr (WithPositions) positions.clear();
return current;
}
if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool> && sizeof(T) <= 8) {
if (n >= 1024) {
using U = std::make_unsigned_t<T>;
struct [[gnu::packed]] Item {
U key;
std::uint32_t index;
};
std::vector<Item> order(n);
U lo = std::numeric_limits<U>::max(), hi = 0;
constexpr U sign = std::is_signed_v<T> ? U(1) << (sizeof(T) * 8 - 1) : U(0);
for (int i = 0; i < n; ++i) {
U key = U(a[i]) ^ sign;
order[i] = {key, std::uint32_t(i)};
lo = std::min(lo, key);
hi = std::max(hi, key);
}
if (hi - lo < 65536 && std::uint64_t(hi - lo) < unsigned(n)) {
std::vector<int> codes(std::size_t(hi - lo) + 1, -1);
for (const auto &v : order) codes[v.key - lo] = 0;
values.clear();
values.reserve(codes.size());
for (std::size_t i = 0; i < codes.size(); ++i)
if (codes[i] == 0) {
codes[i] = static_cast<int>(values.size());
values.push_back(T(U(lo + U(i)) ^ sign));
}
for (const auto &v : order) current[v.index] = codes[v.key - lo];
if (values.capacity() > 2 * values.size()) values.shrink_to_fit();
if constexpr (WithPositions) positions.clear();
return current;
}
std::vector<Item> next(n);
int width = std::bit_width(U(lo ^ hi)), span_width = std::bit_width(U(hi - lo));
if ((width + 10) / 11 > (span_width + 10) / 11) {
for (auto &v : order) v.key -= lo;
width = span_width;
}
for (int shift = 0; shift < width; shift += 11) {
std::array<int, 2048> offsets{};
for (const auto &v : order) ++offsets[(v.key >> shift) & 2047];
int total = 0;
for (int &c : offsets) {
int t = c;
c = total;
total += t;
}
for (const auto &v : order) next[offsets[(v.key >> shift) & 2047]++] = v;
order.swap(next);
}
values.clear();
values.reserve(n);
U previous = 0;
if constexpr (WithPositions) {
positions.resize(n);
offsets.reserve(std::size_t(n) + 1);
}
for (int j = 0; j < n; ++j) {
const auto &v = order[j];
if (values.empty() || v.key != previous) {
values.push_back(a[v.index]);
if constexpr (WithPositions) offsets.push_back(j);
}
previous = v.key;
current[v.index] = static_cast<int>(values.size()) - 1;
if constexpr (WithPositions) positions[j] = v.index;
}
if constexpr (WithPositions) offsets.push_back(n);
if (values.capacity() > 2 * values.size()) values.shrink_to_fit();
return current;
}
}
values = a;
std::sort(values.begin(), values.end());
values.erase(std::unique(values.begin(), values.end()), values.end());
if (values.capacity() > 2 * values.size()) values.shrink_to_fit();
for (int i = 0; i < n; ++i) current[i] = lower_code<T>(values, false, a[i]);
if constexpr (WithPositions) positions.clear();
return current;
}
template<class T> struct ValueIndex {
int n = 0;
std::vector<T> values;
std::vector<std::uint32_t> original;
void build(const std::vector<T> &a) {
assert(a.size() <= std::size_t(std::numeric_limits<int>::max()));
n = static_cast<int>(a.size());
int ordered = 0;
std::vector<int> positions, offsets;
original = compress<T, false>(a, values, ordered, positions, offsets);
}
std::uint32_t code(const T &x) const { return lower_code(values, false, x); }
};
}
#line 9 "data_structure/offline_range_kth_smallest.hpp"
#include <span>
#line 11 "data_structure/offline_range_kth_smallest.hpp"
template <class T>
std::vector<T> offline_range_kth_smallest(const std::vector<T> &values, std::span<const std::array<int, 3>> queries) {
assert(values.size() <= std::size_t(std::numeric_limits<int>::max()) && queries.size() <= std::size_t(std::numeric_limits<int>::max()));
if (queries.empty()) return {};
offline_range_kth_detail::Frontier frontier(queries.size());
for (std::size_t i = 0; i < queries.size(); ++i) {
auto [l, r, k] = queries[i];
assert(0 <= l && l < r && std::size_t(r) <= values.size() && 0 <= k && k < r - l);
frontier.left[i] = unsigned(l); frontier.right[i] = unsigned(r); frontier.rank[i] = unsigned(k);
}
std::vector<T> sorted;
std::vector<int> positions, offsets;
int ordered = 0;
auto codes = wavelet_detail::compress<T, false>(values, sorted, ordered, positions, offsets);
unsigned bits = std::bit_width(unsigned(sorted.size() - 1));
if (bits && !ordered) offline_range_kth_detail::run(frontier, std::move(codes), bits);
std::vector<T> result;
result.reserve(queries.size());
for (std::size_t i = 0; i < queries.size(); ++i) {
if (ordered) {
auto [l, r, k] = queries[i];
result.push_back(values[ordered > 0 ? l + k : r - 1 - k]);
} else result.push_back(sorted[frontier.answer[i]]);
}
return result;
}