競技プログラミング用 C++ ライブラリ
data_structure/static_range_inversions.hpp · GitHub で見る
共通ページ
数列と半開区間の問い合わせをまとめて渡し、各区間の転倒数を返します。同じ値の組は数えず、入力は変更しません。
#include "lib/data_structure/static_range_inversions.hpp"
std::vector<int> a{3, 1, 2, 1};
std::vector<std::pair<int,int>> ranges{ {0,4}, {1,4}, {2,2} };
auto counts = static_range_inversion_counts(a, ranges); // {4,1,0}
| 操作 | 内容・返り値 | 計算量 |
|---|---|---|
static_range_inversion_counts(a,ranges,compare = std::less<>{}) |
各 [l,r) 内の i < j かつ compare(a[j],a[i]) となる組数を、入力区間順の vector<int64_t> で返す |
\(O(n\sqrt n+n\sqrt q+q)\) |
各区間は 0 <= l <= r <= a.size() とします。空区間の答えは 0 です。列長と区間数は INT_MAX 以下とし、この範囲では答えは符号付き 64 bit に収まります。
入力の数列には const なランダムアクセス・サイズ取得可能な range を使えます。vector・array・span・deque・C 配列や、コピー不能な要素にも対応します。比較関数は厳密弱順序を定めるものを指定します。比較上同値な値同士は数えず、std::greater<>{} なら左の値が右の値より小さい組を数えます。
列長を \(n\)、区間数を \(q\) として、時間は \(O(n\sqrt n+n\sqrt q+q)\)、追加空間は \(O(n+q)\) です。問い合わせが 1 個の場合は、その区間に対する 転倒数 の計算を使います。比較とランダムアクセスを定数時間とします。
#ifndef STATIC_RANGE_INVERSIONS_HPP
#define STATIC_RANGE_INVERSIONS_HPP
#include "../sequence/inversion_count.hpp"
#include "../components/radix_sort.hpp"
#include "mo_order.hpp"
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <limits>
#include <numeric>
#include <ranges>
#include <type_traits>
#include <utility>
#include <vector>
template<class Range, class Compare = std::less<>>
requires std::ranges::random_access_range<const Range> && std::ranges::sized_range<const Range>
std::vector<std::int64_t> static_range_inversion_counts(const Range &values,
const std::vector<std::pair<int, int>> &ranges, Compare compare = {}) {
assert(std::ranges::size(values) <= std::size_t(std::numeric_limits<int>::max()));
assert(ranges.size() <= std::size_t(std::numeric_limits<int>::max()));
const int n = int(std::ranges::size(values)), q = int(ranges.size());
for ([[maybe_unused]] auto [l, r] : ranges) assert(0 <= l && l <= r && r <= n);
std::vector<std::int64_t> answer(q);
if (!q || n < 2) return answer;
auto first = std::ranges::begin(values);
if (q == 1) {
auto [l, r] = ranges[0];
answer[0] = inversion_count(std::ranges::subrange(first + l, first + r), compare);
return answer;
}
using T = std::ranges::range_value_t<const Range>;
std::vector<int> ordered(n), rank(n);
std::iota(ordered.begin(), ordered.end(), 0);
if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool> && sizeof(T) <= 8 &&
(std::is_same_v<Compare, std::less<>> || std::is_same_v<Compare, std::greater<>>)) {
using U = std::make_unsigned_t<T>;
stable_radix_sort(ordered, [&](int i) {
U key = U(first[i]);
if constexpr (std::is_signed_v<T>) key ^= U(1) << (std::numeric_limits<U>::digits - 1);
if constexpr (std::is_same_v<Compare, std::greater<>>) key = U(~key);
return key;
});
} else {
std::sort(ordered.begin(), ordered.end(), [&](int a, int b) {
if (compare(first[a], first[b])) return true;
return !compare(first[b], first[a]) && a < b;
});
}
for (int i = 0; i < n; ++i) rank[ordered[i]] = i;
auto queries = mo_order(ranges);
struct Event {
std::size_t next;
int left, right, query, kind;
};
constexpr std::size_t none = std::numeric_limits<std::size_t>::max();
std::vector<std::size_t> heads(n, none);
std::vector<Event> events;
events.reserve(std::size_t(q) * 2);
auto add = [&](int sweep, int l, int r, int sign, bool length, int query) {
if (sweep >= 0) {
events.push_back({heads[sweep], l, r, query, sign * (1 + int(length))});
heads[sweep] = events.size() - 1;
}
};
int left = 0, right = 0;
for (int i : queries) {
auto [l, r] = ranges[i];
if (right < r) add(left - 1, right, r, 1, false, i), right = r;
if (l < left) add(right - 1, l, left, 1, true, i), left = l;
if (r < right) add(left - 1, r, right, -1, false, i), right = r;
if (left < l) add(right - 1, left, l, -1, true, i), left = l;
}
int shift = 0;
while ((std::uint64_t(1) << (2 * shift + 2)) <= std::uint64_t(n)) ++shift;
const int block = 1 << shift;
std::vector<int> direct(n), lazy((std::size_t(n) + block - 1) >> shift);
std::vector<std::int64_t> prefix(std::size_t(n) + 1), delta(q);
for (int sweep = 0; sweep < n; ++sweep) {
const int key = rank[sweep], group = key >> shift;
prefix[sweep + 1] = prefix[sweep] + direct[key] + lazy[group];
for (int i = 0; i < group; ++i) ++lazy[i];
for (int i = group << shift; i <= key; ++i) ++direct[i];
for (std::size_t i = heads[sweep]; i != none; i = events[i].next) {
const auto &event = events[i];
std::int64_t sum = 0;
for (int p = event.left; p < event.right; ++p) {
int key = rank[p];
sum += int(event.kind == 2 || event.kind == -2) * (sweep - p + 1) - direct[key] - lazy[key >> shift];
}
delta[event.query] += event.kind > 0 ? sum : -sum;
}
}
std::int64_t sum = 0;
for (int i : queries) {
sum += delta[i];
auto [l, r] = ranges[i];
answer[i] = sum + prefix[r] - prefix[l];
}
return answer;
}
#endif
#line 1 "data_structure/static_range_inversions.hpp"
#line 1 "sequence/inversion_count.hpp"
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <limits>
#include <ranges>
#include <type_traits>
#include <vector>
template<class Range, class Compare = std::less<>>
requires std::ranges::random_access_range<const Range> && std::ranges::sized_range<const Range>
std::int64_t inversion_count(const Range &a, Compare compare = {}) {
assert(std::ranges::size(a) <= std::size_t(std::numeric_limits<int>::max()));
const std::size_t n = std::ranges::size(a);
auto first = std::ranges::begin(a);
std::size_t ordered = 1;
while (ordered < n && !compare(first[ordered], first[ordered - 1])) ++ordered;
if (ordered >= n) return 0;
std::int64_t equal_pairs = 0;
std::size_t run = 1, reversed = 1;
for (; reversed < n && !compare(first[reversed - 1], first[reversed]); ++reversed) {
if (compare(first[reversed], first[reversed - 1])) run = 1;
else equal_pairs += static_cast<std::int64_t>(run++);
}
if (reversed == n) return static_cast<std::int64_t>(n) * (n - 1) / 2 - equal_pairs;
using T = std::ranges::range_value_t<const Range>;
constexpr bool cached = std::is_trivially_copyable_v<T> && sizeof(T) <= 16 &&
std::is_default_constructible_v<T> && std::is_copy_constructible_v<T> &&
std::is_copy_assignable_v<T> && !std::is_same_v<T, bool>;
using Stored = std::conditional_t<cached, T, int>;
std::vector<Stored> current(n), buffer(n);
for (std::size_t i = 0; i < n; ++i) {
if constexpr (cached) current[i] = first[i];
else current[i] = static_cast<int>(i);
}
auto value = [&](const Stored &x) -> decltype(auto) {
if constexpr (cached) return (x);
else return first[x];
};
std::int64_t result = 0;
constexpr std::size_t block = 8;
for (std::size_t l = 0; l < n; l += block) {
const auto end = std::min(l + block, n);
for (std::size_t i = l + 1; i < end; ++i) {
Stored x = current[i];
std::size_t j = i;
while (j > l && compare(value(x), value(current[j - 1]))) current[j] = current[j - 1], --j;
current[j] = x;
result += static_cast<std::int64_t>(i - j);
}
}
for (std::size_t width = block; width < n; width *= 2) {
for (std::size_t l = 0; l < n; l += 2 * width) {
const auto mid = std::min(l + width, n), r = std::min(l + 2 * width, n);
if (mid == r || !compare(value(current[mid]), value(current[mid - 1]))) {
std::copy(current.begin() + l, current.begin() + r, buffer.begin() + l);
continue;
}
if (compare(value(current[r - 1]), value(current[l]))) {
result += static_cast<std::int64_t>(mid - l) * (r - mid);
std::copy(current.begin() + mid, current.begin() + r, buffer.begin() + l);
std::copy(current.begin() + l, current.begin() + mid, buffer.begin() + (l + r - mid));
continue;
}
std::size_t i = l, j = mid, k = l;
while (i < mid && j < r) {
if (compare(value(current[j]), value(current[i]))) {
buffer[k++] = current[j++];
result += static_cast<std::int64_t>(mid - i);
} else buffer[k++] = current[i++];
}
std::copy(current.begin() + i, current.begin() + mid, buffer.begin() + k);
std::copy(current.begin() + j, current.begin() + r, buffer.begin() + (k + mid - i));
}
current.swap(buffer);
}
return result;
}
#line 1 "components/radix_sort.hpp"
#line 5 "components/radix_sort.hpp"
#include <array>
#line 9 "components/radix_sort.hpp"
#include <utility>
#line 11 "components/radix_sort.hpp"
template <int Bits, class T, class Key>
void stable_radix_sort(std::vector<T> &values, Key &&key) {
static_assert(1 <= Bits && Bits <= 16);
constexpr unsigned buckets = 1U << Bits, mask = buckets - 1;
using K = std::remove_cvref_t<decltype(key(std::declval<const T &>()))>;
static_assert(std::is_integral_v<K> && std::is_unsigned_v<K> && sizeof(K) <= sizeof(std::uint64_t));
if (values.size() < 2) return;
if (values.size() < 256) {
std::stable_sort(values.begin(), values.end(), [&](const T &a, const T &b) { return key(a) < key(b); });
return;
}
std::uint64_t first = key(values.front()), previous = first, varying = 0;
bool sorted = true;
for (std::size_t i = 1; i < values.size(); ++i) {
std::uint64_t current = key(values[i]);
varying |= first ^ current;
sorted &= previous <= current;
previous = current;
}
if (sorted) return;
std::vector<T> scratch(values.size());
using Counts = std::conditional_t<(Bits <= 11), std::array<std::size_t, buckets>, std::vector<std::size_t>>;
Counts count = [] {
if constexpr (Bits <= 11) return Counts{};
else return Counts(buckets);
}();
for (int shift = 0; shift < 64; shift += Bits) {
if (!((varying >> shift) & mask)) continue;
std::fill(count.begin(), count.end(), 0);
for (const auto &value : values) ++count[(static_cast<std::uint64_t>(key(value)) >> shift) & mask];
std::size_t sum = 0;
for (auto &x : count) {
auto n = x;
x = sum, sum += n;
}
for (auto &&value : values) scratch[count[(static_cast<std::uint64_t>(key(value)) >> shift) & mask]++] = std::move(value);
values.swap(scratch);
}
}
template <class T, class Key>
void stable_radix_sort(std::vector<T> &values, Key key) {
stable_radix_sort<8>(values, std::move(key));
}
namespace radix_sort_detail {
template <int Bits = 8, class T, class Key>
void sort_by_key(std::vector<T> &values, Key key) {
using K = std::remove_cvref_t<decltype(key(std::declval<const T &>()))>;
if constexpr (std::is_integral_v<K> && !std::is_same_v<K, bool> && sizeof(K) <= 8 && std::is_default_constructible_v<T>) {
using U = std::make_unsigned_t<K>;
constexpr U sign = std::is_signed_v<K> ? U(1) << (sizeof(K) * 8 - 1) : U(0);
stable_radix_sort<Bits>(values, [&](const T &value) { return U(U(key(value)) ^ sign); });
} else std::sort(values.begin(), values.end(), [&](const T &a, const T &b) { return key(a) < key(b); });
}
}
#line 1 "data_structure/mo_order.hpp"
#line 7 "data_structure/mo_order.hpp"
#include <cmath>
#line 12 "data_structure/mo_order.hpp"
inline std::vector<int> mo_order(const std::vector<std::pair<int, int>>& ranges, int block_size = 0) {
assert(ranges.size() <= std::size_t(std::numeric_limits<int>::max()) && block_size >= 0);
if (ranges.empty()) return {};
int min_l = ranges[0].first, max_l = min_l, min_r = ranges[0].second, max_r = min_r;
for (auto [l, r] : ranges) {
assert(0 <= l && l <= r);
min_l = std::min(min_l, l), max_l = std::max(max_l, l);
min_r = std::min(min_r, r), max_r = std::max(max_r, r);
}
if (!block_size) block_size = std::max(1, int(std::sqrt((double(max_l) - min_l + 1) * (double(max_r) - min_r + 1) / ranges.size())));
std::vector<std::pair<std::uint64_t, int>> keys;
keys.reserve(ranges.size());
for (int i = 0; i < int(ranges.size()); ++i) {
auto [l, r] = ranges[i];
std::uint32_t block = (l - min_l) / block_size;
std::uint32_t right = block & 1 ? ~std::uint32_t(r) : std::uint32_t(r);
keys.emplace_back((std::uint64_t(block) << 32) | right, i);
}
if (keys.size() < 2048) std::sort(keys.begin(), keys.end());
else stable_radix_sort(keys, [](const auto &x) { return x.first; });
std::vector<int> order;
order.reserve(keys.size());
for (auto [key, i] : keys) { (void)key; order.push_back(i); }
return order;
}
#line 13 "data_structure/static_range_inversions.hpp"
#include <numeric>
#line 18 "data_structure/static_range_inversions.hpp"
template<class Range, class Compare = std::less<>>
requires std::ranges::random_access_range<const Range> && std::ranges::sized_range<const Range>
std::vector<std::int64_t> static_range_inversion_counts(const Range &values,
const std::vector<std::pair<int, int>> &ranges, Compare compare = {}) {
assert(std::ranges::size(values) <= std::size_t(std::numeric_limits<int>::max()));
assert(ranges.size() <= std::size_t(std::numeric_limits<int>::max()));
const int n = int(std::ranges::size(values)), q = int(ranges.size());
for ([[maybe_unused]] auto [l, r] : ranges) assert(0 <= l && l <= r && r <= n);
std::vector<std::int64_t> answer(q);
if (!q || n < 2) return answer;
auto first = std::ranges::begin(values);
if (q == 1) {
auto [l, r] = ranges[0];
answer[0] = inversion_count(std::ranges::subrange(first + l, first + r), compare);
return answer;
}
using T = std::ranges::range_value_t<const Range>;
std::vector<int> ordered(n), rank(n);
std::iota(ordered.begin(), ordered.end(), 0);
if constexpr (std::is_integral_v<T> && !std::is_same_v<T, bool> && sizeof(T) <= 8 &&
(std::is_same_v<Compare, std::less<>> || std::is_same_v<Compare, std::greater<>>)) {
using U = std::make_unsigned_t<T>;
stable_radix_sort(ordered, [&](int i) {
U key = U(first[i]);
if constexpr (std::is_signed_v<T>) key ^= U(1) << (std::numeric_limits<U>::digits - 1);
if constexpr (std::is_same_v<Compare, std::greater<>>) key = U(~key);
return key;
});
} else {
std::sort(ordered.begin(), ordered.end(), [&](int a, int b) {
if (compare(first[a], first[b])) return true;
return !compare(first[b], first[a]) && a < b;
});
}
for (int i = 0; i < n; ++i) rank[ordered[i]] = i;
auto queries = mo_order(ranges);
struct Event {
std::size_t next;
int left, right, query, kind;
};
constexpr std::size_t none = std::numeric_limits<std::size_t>::max();
std::vector<std::size_t> heads(n, none);
std::vector<Event> events;
events.reserve(std::size_t(q) * 2);
auto add = [&](int sweep, int l, int r, int sign, bool length, int query) {
if (sweep >= 0) {
events.push_back({heads[sweep], l, r, query, sign * (1 + int(length))});
heads[sweep] = events.size() - 1;
}
};
int left = 0, right = 0;
for (int i : queries) {
auto [l, r] = ranges[i];
if (right < r) add(left - 1, right, r, 1, false, i), right = r;
if (l < left) add(right - 1, l, left, 1, true, i), left = l;
if (r < right) add(left - 1, r, right, -1, false, i), right = r;
if (left < l) add(right - 1, left, l, -1, true, i), left = l;
}
int shift = 0;
while ((std::uint64_t(1) << (2 * shift + 2)) <= std::uint64_t(n)) ++shift;
const int block = 1 << shift;
std::vector<int> direct(n), lazy((std::size_t(n) + block - 1) >> shift);
std::vector<std::int64_t> prefix(std::size_t(n) + 1), delta(q);
for (int sweep = 0; sweep < n; ++sweep) {
const int key = rank[sweep], group = key >> shift;
prefix[sweep + 1] = prefix[sweep] + direct[key] + lazy[group];
for (int i = 0; i < group; ++i) ++lazy[i];
for (int i = group << shift; i <= key; ++i) ++direct[i];
for (std::size_t i = heads[sweep]; i != none; i = events[i].next) {
const auto &event = events[i];
std::int64_t sum = 0;
for (int p = event.left; p < event.right; ++p) {
int key = rank[p];
sum += int(event.kind == 2 || event.kind == -2) * (sweep - p + 1) - direct[key] - lazy[key >> shift];
}
delta[event.query] += event.kind > 0 ? sum : -sum;
}
}
std::int64_t sum = 0;
for (int i : queries) {
sum += delta[i];
auto [l, r] = ranges[i];
answer[i] = sum + prefix[r] - prefix[l];
}
return answer;
}