競技プログラミング用 C++ ライブラリ
components/packed_convex_hull.hpp · GitHub で見る
共通ページ
凸包で使う、整数座標の値域を確認してから適用する共通処理です。座標を平行移動し、radix sort と整数の向き判定で凸包を求めます。対応しない値域は呼び出し元の汎用処理を使います。
点数を \(n\) として、時間・追加空間は \(O(n)\) です。
#ifndef PACKED_CONVEX_HULL_HPP
#define PACKED_CONVEX_HULL_HPP
#include "radix_sort.hpp"
#include <algorithm>
#include <cstdint>
#include <vector>
#include <utility>
namespace packed_convex_hull_detail {
struct Entry {
std::uint64_t key;
int index;
};
inline std::int64_t cross(std::uint64_t a, std::uint64_t b, std::uint64_t c) {
std::int64_t x = a >> 32, y = std::uint32_t(a);
return (std::int64_t(b >> 32) - x) * (std::int64_t(std::uint32_t(c)) - y)
- (std::int64_t(std::uint32_t(b)) - y) * (std::int64_t(c >> 32) - x);
}
inline std::uint64_t key(std::uint64_t value) { return value; }
inline std::uint64_t key(Entry value) { return value.key; }
template <class Value>
std::vector<Value> build(std::vector<Value> order, bool keep_collinear) {
if (order.size() < 256) std::sort(order.begin(), order.end(), [](const Value &a, const Value &b) { return key(a) < key(b); });
else stable_radix_sort<11>(order, [](const Value &value) { return key(value); });
order.erase(std::unique(order.begin(), order.end(), [](const Value &a, const Value &b) { return key(a) == key(b); }), order.end());
if (order.size() <= 2 || (keep_collinear && std::all_of(order.begin() + 2, order.end(), [&](const Value &value) {
return !cross(key(order[0]), key(order[1]), key(value));
}))) {
return order;
}
std::vector<Value> hull;
hull.reserve(order.size() * 2);
auto append = [&](Value value, std::size_t base) {
while (hull.size() >= base) {
auto turn = cross(key(hull[hull.size() - 2]), key(hull.back()), key(value));
if (turn > 0 || (keep_collinear && !turn)) break;
hull.pop_back();
}
hull.push_back(value);
};
for (auto value : order) append(value, 2);
std::size_t base = hull.size() + 1;
for (std::size_t i = order.size() - 1; i-- > 0;) append(order[i], base);
hull.pop_back();
return hull;
}
inline std::pair<std::uint64_t, std::uint64_t> diameter(const std::vector<std::uint64_t> &hull) {
std::size_t n = hull.size();
std::uint64_t a = hull[0], b = a, best = 0;
auto distance = [](std::uint64_t p, std::uint64_t q) {
std::int64_t x = std::int64_t(p >> 32) - std::int64_t(q >> 32);
std::int64_t y = std::int64_t(std::uint32_t(p)) - std::uint32_t(q);
return std::uint64_t(x * x + y * y);
};
for (std::size_t i = 0, j = n > 1; i < n; ++i) {
std::size_t next = i + 1 == n ? 0 : i + 1;
while (true) {
std::size_t after = j + 1 == n ? 0 : j + 1;
if (cross(hull[i], hull[next], hull[after]) <= cross(hull[i], hull[next], hull[j])) break;
j = after;
}
for (auto p : {hull[i], hull[next]}) {
auto d = distance(p, hull[j]);
if (d > best) a = p, b = hull[j], best = d;
}
}
return {a, b};
}
}
#endif
#line 1 "components/packed_convex_hull.hpp"
#line 1 "components/radix_sort.hpp"
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <type_traits>
#include <utility>
#include <vector>
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 9 "components/packed_convex_hull.hpp"
namespace packed_convex_hull_detail {
struct Entry {
std::uint64_t key;
int index;
};
inline std::int64_t cross(std::uint64_t a, std::uint64_t b, std::uint64_t c) {
std::int64_t x = a >> 32, y = std::uint32_t(a);
return (std::int64_t(b >> 32) - x) * (std::int64_t(std::uint32_t(c)) - y)
- (std::int64_t(std::uint32_t(b)) - y) * (std::int64_t(c >> 32) - x);
}
inline std::uint64_t key(std::uint64_t value) { return value; }
inline std::uint64_t key(Entry value) { return value.key; }
template <class Value>
std::vector<Value> build(std::vector<Value> order, bool keep_collinear) {
if (order.size() < 256) std::sort(order.begin(), order.end(), [](const Value &a, const Value &b) { return key(a) < key(b); });
else stable_radix_sort<11>(order, [](const Value &value) { return key(value); });
order.erase(std::unique(order.begin(), order.end(), [](const Value &a, const Value &b) { return key(a) == key(b); }), order.end());
if (order.size() <= 2 || (keep_collinear && std::all_of(order.begin() + 2, order.end(), [&](const Value &value) {
return !cross(key(order[0]), key(order[1]), key(value));
}))) {
return order;
}
std::vector<Value> hull;
hull.reserve(order.size() * 2);
auto append = [&](Value value, std::size_t base) {
while (hull.size() >= base) {
auto turn = cross(key(hull[hull.size() - 2]), key(hull.back()), key(value));
if (turn > 0 || (keep_collinear && !turn)) break;
hull.pop_back();
}
hull.push_back(value);
};
for (auto value : order) append(value, 2);
std::size_t base = hull.size() + 1;
for (std::size_t i = order.size() - 1; i-- > 0;) append(order[i], base);
hull.pop_back();
return hull;
}
inline std::pair<std::uint64_t, std::uint64_t> diameter(const std::vector<std::uint64_t> &hull) {
std::size_t n = hull.size();
std::uint64_t a = hull[0], b = a, best = 0;
auto distance = [](std::uint64_t p, std::uint64_t q) {
std::int64_t x = std::int64_t(p >> 32) - std::int64_t(q >> 32);
std::int64_t y = std::int64_t(std::uint32_t(p)) - std::uint32_t(q);
return std::uint64_t(x * x + y * y);
};
for (std::size_t i = 0, j = n > 1; i < n; ++i) {
std::size_t next = i + 1 == n ? 0 : i + 1;
while (true) {
std::size_t after = j + 1 == n ? 0 : j + 1;
if (cross(hull[i], hull[next], hull[after]) <= cross(hull[i], hull[next], hull[j])) break;
j = after;
}
for (auto p : {hull[i], hull[next]}) {
auto d = distance(p, hull[j]);
if (d > best) a = p, b = hull[j], best = d;
}
}
return {a, b};
}
}