競技プログラミング用 C++ ライブラリ
components/binomial_prefix_batch.hpp · GitHub で見る
共通ページ
二項係数の prefix sum の通常版・軽量版で共有する内部実装です。利用するときは公開ヘッダーの binomial_prefix_sum を使ってください。
binomial_prefix_detail::batch<Mint,Table>(queries,block_size) は std::vector<Mint> を返します。queries は std::vector<std::pair<int,K>>、K は 64 bit 以下の符号付き整数型です。各要素 {n,k} に対し、右端を含まない和 \(\displaystyle \sum_{i=0}^{k-1}\binom ni\) を求め、入力順に返します。k <= 0 は 0、k >= n+1 は \(2^n\) です。重複クエリも保持し、空入力には空の列を返します。入力は変更しません。
n は非負かつ INT_MAX と法より小さく、block_size は非負の int とします。Mint は素数 mod の要素型、Table は同じ要素型の階乗・逆階乗・2 の累乗と区間の二項係数和を扱う型です。通常版の binomial_prefix_detail::Table と軽量版の light_binomial_prefix_detail::Table を組み合わせられます。
端や中央の直接答えられるクエリを先に処理します。残ったクエリが同じ行にある場合は必要な prefix を一度だけ計算し、異なる行にある場合は Online<Mint,Table> の格子を使います。
残ったクエリの最大 n を \(M\)、個数を \(R\) とすると、幅 0 は \(\displaystyle B_0=\max(1,\operatorname{round}(M/\sqrt R))\) を基準にします。全クエリ数から求めた幅も候補にし、残ったクエリの標本から前計算と補正量を見積もって選びます。選ぶ幅は \(B_0/2\leq B\leq B_0\) の範囲です。正の幅はそのまま使います。格子を省く場合は幅を使いません。
全クエリの最大 n を \(N\)、個数を \(Q\) とすると、時間は \(O(N+Q+M^2/B+RB)\)、空間は \(O(N+Q+M^2/B^2)\) です。幅 0 は \(O(N+Q+M\sqrt R)\) 時間・\(O(N+Q)\) 空間です。格子を省く場合は \(O(N+Q)\) 時間・空間、空入力は \(O(1)\) 時間・空間です。法上の演算を定数時間とし、階乗表には定数回の逆元計算を含みます。
#ifndef BINOMIAL_PREFIX_BATCH_HPP
#define BINOMIAL_PREFIX_BATCH_HPP
#include "binomial_prefix_online.hpp"
#include <limits>
#include <utility>
namespace binomial_prefix_detail {
template<class Mint, class Table, class K>
std::vector<Mint> batch(const std::vector<std::pair<int, K>>& queries, int block_size) {
static_assert(std::is_integral_v<K> && std::is_signed_v<K> && sizeof(K) <= 8);
assert(block_size >= 0 && Mint::mod() > 1);
std::vector<Mint> answer(queries.size());
if (queries.empty()) return answer;
int limit = 0;
for (auto [n, k] : queries) {
(void)k;
assert(0 <= n && n < std::numeric_limits<int>::max() && std::uint64_t(n) < Mint::mod());
limit = std::max(limit, n);
}
std::vector<Mint> power2(std::size_t(limit) + 1);
power2[0] = Mint(1);
for (int n = 1; n <= limit; ++n) power2[n] = power2[n - 1] + power2[n - 1];
struct Point { int n, k; std::size_t id; bool reflect; };
std::vector<Point> points;
points.reserve(queries.size());
int maximum = 0;
for (std::size_t i = 0; i < queries.size(); ++i) {
auto [n, k] = queries[i];
if (k <= 0) continue;
if (std::uint64_t(k) >= std::uint64_t(n) + 1) { answer[i] = power2[n]; continue; }
bool reflect = k > (n + 1) / 2;
int m = reflect ? n + 1 - int(k) : int(k);
if (m == 1) answer[i] = reflect ? power2[n] - Mint(1) : Mint(1);
else if ((n & 1) && m == (n + 1) / 2) answer[i] = power2[n - 1];
else {
points.push_back({n, m, i, reflect});
maximum = std::max(maximum, n);
}
}
if (points.empty()) return answer;
auto store = [&](const Point& point, Mint value) {
answer[point.id] = point.reflect ? power2[point.n] - value : value;
};
if (std::all_of(points.begin(), points.end(), [&](const Point& point) { return point.n == maximum; })) {
Table table(maximum);
int first = maximum, last = 0;
for (const auto& point : points) first = std::min(first, point.k), last = std::max(last, point.k);
std::vector<Mint> prefix(std::size_t(last - first) + 1);
Mint value;
if constexpr (std::is_same_v<Mint, mint>) value = table.factorial[maximum] * mint_detail::inner_product<true>(table.inverse_factorial.data(), table.inverse_factorial.data() + maximum, first);
else value = table.range(maximum, 0, first);
prefix[0] = value;
for (int k = first; k < last; ++k) prefix[k - first + 1] = value += table.binom(maximum, k);
for (const auto& point : points) store(point, prefix[point.k - first]);
} else {
int block = block_size ? block_size : std::max(1, int(std::round(maximum / std::sqrt(double(points.size())))));
if (!block_size) {
int alternative = std::max(1, int(std::round(maximum / std::sqrt(double(queries.size())))));
if (alternative != block && alternative >= block / 2 + block % 2) {
std::size_t count = std::min(points.size(), std::size_t(256));
auto estimate = [&](int width) {
int rows = maximum / width;
double sum = 0;
for (std::size_t i = 0; i < count; ++i) {
const auto& point = points[i * (points.size() / count)];
int row = std::min(int((std::int64_t(point.n) + width / 2) / width), rows);
int column = std::min(int((std::int64_t(point.k - 1) + width / 2) / width), row);
std::int64_t distance = std::abs(point.n - row * width) + std::int64_t(std::abs(point.k - 1 - column * width));
sum += std::min(std::int64_t(std::min(point.k - 1, (point.n + 1) / 2 - point.k)), distance);
}
return double(maximum) * maximum / (4 * double(width)) + double(points.size()) * sum / double(count);
};
if (estimate(alternative) < estimate(block)) block = alternative;
}
}
Online<Mint, Table> online(maximum, block);
for (const auto& point : points) store(point, online.query(point.n, point.k));
}
return answer;
}
}
#endif
#line 1 "components/binomial_prefix_batch.hpp"
#line 1 "components/binomial_prefix_online.hpp"
#line 1 "components/mint_inner_product.hpp"
#line 1 "math/mint.hpp"
#include <cassert>
#include <istream>
#include <ostream>
#include <type_traits>
#if defined(__GNUC__) && !defined(__clang__) && (defined(__x86_64__) || defined(__i386__))
#pragma GCC push_options
#pragma GCC optimize("O3")
#endif
namespace mint_detail {
template <class I>
constexpr bool mint_integral = std::is_integral_v<I>
#if defined(__SIZEOF_INT128__)
|| std::is_same_v<I, __int128> || std::is_same_v<I, unsigned __int128>
#endif
;
struct [[gnu::warn_unused]] Mint {
using mint = Mint;
unsigned v = 0;
[[nodiscard]] static constexpr unsigned mod() { return 998244353; }
[[nodiscard, gnu::always_inline]] static unsigned reduce(unsigned long long x) {
return (x + (unsigned long long)(unsigned(x) * 998244351u) * mod()) >> 32;
}
[[gnu::always_inline]] Mint() = default;
template <class I, std::enable_if_t<mint_integral<I>, int> = 0>
Mint(I x) {
long long r = x % (long long)mod();
if (r < 0) r += mod();
v = reduce((unsigned long long)r * 932051910);
}
[[nodiscard, gnu::always_inline]] static Mint raw(unsigned x) {
Mint a;
a.v = reduce((unsigned long long)x * 932051910);
return a;
}
[[nodiscard, gnu::always_inline]] static Mint word(unsigned x) {
Mint a;
a.v = x;
return a;
}
[[nodiscard, gnu::always_inline]] unsigned val() const {
unsigned r = reduce(v);
return r < mod() ? r : r - mod();
}
friend std::istream &operator>>(std::istream &is, Mint &x) {
long long value;
if (is >> value) x = Mint(value);
return is;
}
friend std::ostream &operator<<(std::ostream &os, const Mint &x) {
return os << x.val();
}
[[gnu::always_inline]] Mint &operator+=(Mint b) {
v += b.v;
if (v >= 2 * mod()) v -= 2 * mod();
return *this;
}
[[gnu::always_inline]] Mint &operator-=(Mint b) {
v -= b.v;
if (v >> 31) v += 2 * mod();
return *this;
}
[[gnu::always_inline]] Mint &operator*=(Mint b) {
v = reduce((unsigned long long)v * b.v);
return *this;
}
Mint &operator/=(Mint b) { return *this *= b.inv(); }
Mint &operator++() { return *this += Mint(1); }
Mint &operator--() { return *this -= Mint(1); }
Mint operator++(int) {
Mint a = *this;
++*this;
return a;
}
Mint operator--(int) {
Mint a = *this;
--*this;
return a;
}
[[nodiscard, gnu::always_inline]] Mint operator+() const { return *this; }
[[nodiscard, gnu::always_inline]] Mint operator-() const { return word(v ? 2 * mod() - v : 0); }
[[nodiscard, gnu::always_inline]] Mint operator+(Mint b) const { return Mint(*this) += b; }
[[nodiscard, gnu::always_inline]] Mint operator-(Mint b) const { return Mint(*this) -= b; }
[[nodiscard, gnu::always_inline]] Mint operator*(Mint b) const { return Mint(*this) *= b; }
[[nodiscard]] Mint operator/(Mint b) const { return Mint(*this) /= b; }
[[nodiscard, gnu::always_inline]] bool operator==(Mint b) const { return (v < mod() ? v : v - mod()) == (b.v < mod() ? b.v : b.v - mod()); }
[[nodiscard, gnu::always_inline]] bool operator!=(Mint b) const { return !(*this == b); }
template <class I, std::enable_if_t<mint_integral<I>, int> = 0>
[[nodiscard]] friend Mint operator+(I a, Mint b) { return Mint(a) + b; }
template <class I, std::enable_if_t<mint_integral<I>, int> = 0>
[[nodiscard]] friend Mint operator-(I a, Mint b) { return Mint(a) - b; }
template <class I, std::enable_if_t<mint_integral<I>, int> = 0>
[[nodiscard]] friend Mint operator*(I a, Mint b) { return Mint(a) * b; }
template <class I, std::enable_if_t<mint_integral<I>, int> = 0>
[[nodiscard]] friend Mint operator/(I a, Mint b) { return Mint(a) / b; }
template <class I, std::enable_if_t<mint_integral<I>, int> = 0>
[[nodiscard]] friend bool operator==(I a, Mint b) { return Mint(a) == b; }
template <class I, std::enable_if_t<mint_integral<I>, int> = 0>
[[nodiscard]] friend bool operator!=(I a, Mint b) { return Mint(a) != b; }
[[nodiscard]] Mint pow(long long n) const {
assert(n >= 0);
if (!n) return Mint(1);
Mint r(1), a = *this;
for (; n > 1; n >>= 1, a *= a)
if (n & 1) r *= a;
return r * a;
}
[[nodiscard, gnu::noinline]] Mint inv() const {
assert(*this != Mint(0));
Mint a2 = *this * *this, a3 = a2 * *this;
Mint a7 = a3 * a3 * *this;
Mint a31 = a7;
for (int i = 0; i < 2; ++i) a31 *= a31;
a31 *= a3;
Mint r = a7;
for (int i = 0; i < 3; ++i) r *= r;
r *= a3;
r *= r;
for (int j = 0; j < 4; ++j) {
for (int i = 0; i < 5; ++i) r *= r;
r *= a31;
}
for (int i = 0; i < 3; ++i) r *= r;
return r * a7;
}
};
} // namespace mint_detail
using mint = mint_detail::Mint;
using fps_mint = mint;
#if defined(__GNUC__) && !defined(__clang__) && (defined(__x86_64__) || defined(__i386__))
#pragma GCC pop_options
#endif
#line 5 "components/mint_inner_product.hpp"
#include <cstdint>
#if (defined(__GNUC__) || defined(__clang__)) && (defined(__x86_64__) || defined(__i386__))
#include <immintrin.h>
#endif
namespace mint_detail {
template<bool Reverse> inline __uint128_t inner_product_scalar(const mint* a, const mint* b, int size) {
__uint128_t sum = 0;
int i = 0;
for (; i <= size - 4; i += 4) {
std::uint64_t part = std::uint64_t(a[i].v) * b[Reverse ? -i : i].v
+ std::uint64_t(a[i + 1].v) * b[Reverse ? -i - 1 : i + 1].v
+ std::uint64_t(a[i + 2].v) * b[Reverse ? -i - 2 : i + 2].v
+ std::uint64_t(a[i + 3].v) * b[Reverse ? -i - 3 : i + 3].v;
sum += part;
}
for (; i < size; ++i) sum += std::uint64_t(a[i].v) * b[Reverse ? -i : i].v;
return sum;
}
#if (defined(__GNUC__) || defined(__clang__)) && (defined(__x86_64__) || defined(__i386__))
inline bool inner_product_has_avx2() {
#if defined(__AVX2__)
return true;
#else
static const bool enabled = __builtin_cpu_supports("avx2");
return enabled;
#endif
}
inline bool inner_product_has_avx512() {
#if defined(__AVX512F__)
return true;
#else
static const bool enabled = __builtin_cpu_supports("avx512f");
return enabled;
#endif
}
template<bool Reverse> __attribute__((target("avx2")))
__uint128_t inner_product_avx2(const mint* a, const mint* b, int size) {
const auto order = _mm256_setr_epi32(7, 6, 5, 4, 3, 2, 1, 0);
const auto zero = _mm256_setzero_si256();
auto total = zero, carry = zero;
int i = 0;
for (; i <= size - 8; i += 8) {
auto x = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(a + i));
auto y = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(b + (Reverse ? -i - 7 : i)));
if constexpr (Reverse) y = _mm256_permutevar8x32_epi32(y, order);
auto even = _mm256_mul_epu32(x, y);
auto odd = _mm256_mul_epu32(_mm256_srli_epi64(x, 32), _mm256_srli_epi64(y, 32));
auto next = _mm256_add_epi64(total, _mm256_add_epi64(even, odd));
carry = _mm256_sub_epi64(carry, _mm256_cmpgt_epi64(zero, _mm256_andnot_si256(next, total)));
total = next;
}
alignas(32) std::uint64_t low[4], high[4];
_mm256_store_si256(reinterpret_cast<__m256i*>(low), total);
_mm256_store_si256(reinterpret_cast<__m256i*>(high), carry);
__uint128_t sum = 0;
if (i < size) sum = inner_product_scalar<Reverse>(a + i, b + (Reverse ? -i : i), size - i);
for (int j = 0; j < 4; ++j) sum += (__uint128_t(high[j]) << 64) + low[j];
return sum;
}
template<bool Reverse> __attribute__((target("avx512f")))
__uint128_t inner_product_avx512(const mint* a, const mint* b, int size) {
const auto order = _mm512_setr_epi32(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0);
const auto zero = _mm512_setzero_si512();
const auto one = _mm512_set1_epi64(1);
auto total = zero, carry = zero;
int i = 0;
for (; i <= size - 16; i += 16) {
auto x = _mm512_loadu_si512(a + i);
auto y = _mm512_loadu_si512(b + (Reverse ? -i - 15 : i));
if constexpr (Reverse) y = _mm512_permutexvar_epi32(order, y);
auto even = _mm512_mul_epu32(x, y);
auto odd = _mm512_mul_epu32(_mm512_srli_epi64(x, 32), _mm512_srli_epi64(y, 32));
auto next = _mm512_add_epi64(total, _mm512_add_epi64(even, odd));
carry = _mm512_mask_add_epi64(carry, _mm512_cmplt_epu64_mask(next, total), carry, one);
total = next;
}
alignas(64) std::uint64_t low[8], high[8];
_mm512_store_si512(low, total);
_mm512_store_si512(high, carry);
__uint128_t sum = 0;
if (i < size) sum = inner_product_scalar<Reverse>(a + i, b + (Reverse ? -i : i), size - i);
for (int j = 0; j < 8; ++j) sum += (__uint128_t(high[j]) << 64) + low[j];
return sum;
}
#endif
template<bool Reverse = false> mint inner_product(const mint* a, const mint* b, int size) {
assert(size >= 0);
__uint128_t sum;
#if (defined(__GNUC__) || defined(__clang__)) && (defined(__x86_64__) || defined(__i386__))
if (size >= 64 && inner_product_has_avx512()) sum = inner_product_avx512<Reverse>(a, b, size);
else if (size >= 64 && inner_product_has_avx2()) sum = inner_product_avx2<Reverse>(a, b, size);
else
#endif
sum = inner_product_scalar<Reverse>(a, b, size);
return mint::word(mint::reduce(std::uint64_t(sum % mint::mod())));
}
}
#line 5 "components/binomial_prefix_online.hpp"
#include <algorithm>
#line 7 "components/binomial_prefix_online.hpp"
#include <cmath>
#include <cstddef>
#line 11 "components/binomial_prefix_online.hpp"
#include <vector>
namespace binomial_prefix_detail {
template<class Mint, class Table> class Online {
Table table_;
int max_n_ = 0, block_ = 1;
std::vector<Mint> scaled_factorial_, data_;
void check([[maybe_unused]] int n) const { assert(0 <= n && n <= max_n_); }
static std::size_t offset(int row) { return std::size_t(row) * (std::size_t(row) + 1) / 2; }
template<bool Reverse> static Mint dot(const Mint* a, const Mint* b, int size) {
if constexpr (std::is_same_v<Mint, mint>) {
return mint_detail::inner_product<Reverse>(a, b, size);
} else {
Mint sum(0);
for (int i = 0; i < size; ++i) sum += a[i] * b[Reverse ? -i : i];
return sum;
}
}
Mint range(int n, int l, int r) const {
return table_.factorial[n] * dot<true>(table_.inverse_factorial.data() + l, table_.inverse_factorial.data() + n - l, r - l);
}
public:
explicit Online(int max_n = 0, int block_size = 0) { build(max_n, block_size); }
void build(int max_n, int block_size = 0) {
assert(max_n >= 0 && block_size >= 0);
table_.build(max_n);
max_n_ = max_n;
block_ = block_size ? block_size : std::max(1, int(std::sqrt(double(max_n) + 1)));
if (max_n < 2) { scaled_factorial_.clear(); data_.clear(); return; }
scaled_factorial_.resize(std::size_t(max_n) + 1);
Mint inverse_power = table_.power2[max_n].inv() * table_.half;
for (int i = max_n; i >= 0; --i) {
scaled_factorial_[i] = table_.factorial[i] * inverse_power;
inverse_power += inverse_power;
}
int rows = max_n / block_;
data_.resize(offset(rows) + std::size_t(rows) + 1);
for (int row = 0; row <= rows; ++row) {
int n = row * block_;
Mint factor = scaled_factorial_[n] + scaled_factorial_[n], sum(0);
auto base = offset(row);
for (int column = 0; column <= row / 2; ++column) {
int k = column * block_;
if (column) sum += dot<true>(table_.inverse_factorial.data() + k - block_, table_.inverse_factorial.data() + n - k + block_, block_);
Mint value = sum * factor;
data_[base + row - column] = Mint(1) - value;
data_[base + column] = value + table_.inverse_factorial[k] * table_.inverse_factorial[n - k] * factor;
}
}
}
int max_n() const { return max_n_; }
int block_size() const { return block_; }
std::size_t table_size() const { return data_.size(); }
Mint query(int n, std::int64_t k) const {
check(n);
if (k <= 0) return Mint(0);
if (k >= std::int64_t(n) + 1) return table_.power2[n];
bool reflect = k > (n + 1) / 2;
int m = reflect ? n + 1 - int(k) : int(k);
Mint value(1);
if (m != 1) {
if (m == (n + 1) / 2) value = table_.center(n);
else {
int row = std::min(int((std::int64_t(n) + block_ / 2) / block_), max_n_ / block_);
int column = std::min(int((std::int64_t(m - 1) + block_ / 2) / block_), row);
int start = row * block_, anchor = column * block_;
int center = (n + 1) / 2;
auto distance = std::int64_t(std::abs(n - start)) + std::abs(m - 1 - anchor);
if (std::min(m - 1, center - m) <= distance) {
value = m - 1 <= center - m ? Mint(1) + range(n, 1, m) : table_.center(n) - range(n, m, center);
} else {
value = data_[offset(row) + column];
if (start < n) value -= table_.inverse_factorial[anchor] * dot<false>(scaled_factorial_.data() + start, table_.inverse_factorial.data() + start - anchor, n - start);
else if (n < start) {
int first = std::max(n, anchor);
value += table_.inverse_factorial[anchor] * dot<false>(scaled_factorial_.data() + first, table_.inverse_factorial.data() + first - anchor, start - first);
}
value *= table_.power2[n];
if (anchor < m - 1) value += range(n, anchor + 1, m);
else if (m - 1 < anchor) value -= range(n, m, std::min(anchor, n) + 1);
}
}
}
return reflect ? table_.power2[n] - value : value;
}
Mint prod(int n, std::int64_t l, std::int64_t r) const { assert(l <= r); return query(n, r) - query(n, l); }
};
}
#line 5 "components/binomial_prefix_batch.hpp"
#include <limits>
#include <utility>
namespace binomial_prefix_detail {
template<class Mint, class Table, class K>
std::vector<Mint> batch(const std::vector<std::pair<int, K>>& queries, int block_size) {
static_assert(std::is_integral_v<K> && std::is_signed_v<K> && sizeof(K) <= 8);
assert(block_size >= 0 && Mint::mod() > 1);
std::vector<Mint> answer(queries.size());
if (queries.empty()) return answer;
int limit = 0;
for (auto [n, k] : queries) {
(void)k;
assert(0 <= n && n < std::numeric_limits<int>::max() && std::uint64_t(n) < Mint::mod());
limit = std::max(limit, n);
}
std::vector<Mint> power2(std::size_t(limit) + 1);
power2[0] = Mint(1);
for (int n = 1; n <= limit; ++n) power2[n] = power2[n - 1] + power2[n - 1];
struct Point { int n, k; std::size_t id; bool reflect; };
std::vector<Point> points;
points.reserve(queries.size());
int maximum = 0;
for (std::size_t i = 0; i < queries.size(); ++i) {
auto [n, k] = queries[i];
if (k <= 0) continue;
if (std::uint64_t(k) >= std::uint64_t(n) + 1) { answer[i] = power2[n]; continue; }
bool reflect = k > (n + 1) / 2;
int m = reflect ? n + 1 - int(k) : int(k);
if (m == 1) answer[i] = reflect ? power2[n] - Mint(1) : Mint(1);
else if ((n & 1) && m == (n + 1) / 2) answer[i] = power2[n - 1];
else {
points.push_back({n, m, i, reflect});
maximum = std::max(maximum, n);
}
}
if (points.empty()) return answer;
auto store = [&](const Point& point, Mint value) {
answer[point.id] = point.reflect ? power2[point.n] - value : value;
};
if (std::all_of(points.begin(), points.end(), [&](const Point& point) { return point.n == maximum; })) {
Table table(maximum);
int first = maximum, last = 0;
for (const auto& point : points) first = std::min(first, point.k), last = std::max(last, point.k);
std::vector<Mint> prefix(std::size_t(last - first) + 1);
Mint value;
if constexpr (std::is_same_v<Mint, mint>) value = table.factorial[maximum] * mint_detail::inner_product<true>(table.inverse_factorial.data(), table.inverse_factorial.data() + maximum, first);
else value = table.range(maximum, 0, first);
prefix[0] = value;
for (int k = first; k < last; ++k) prefix[k - first + 1] = value += table.binom(maximum, k);
for (const auto& point : points) store(point, prefix[point.k - first]);
} else {
int block = block_size ? block_size : std::max(1, int(std::round(maximum / std::sqrt(double(points.size())))));
if (!block_size) {
int alternative = std::max(1, int(std::round(maximum / std::sqrt(double(queries.size())))));
if (alternative != block && alternative >= block / 2 + block % 2) {
std::size_t count = std::min(points.size(), std::size_t(256));
auto estimate = [&](int width) {
int rows = maximum / width;
double sum = 0;
for (std::size_t i = 0; i < count; ++i) {
const auto& point = points[i * (points.size() / count)];
int row = std::min(int((std::int64_t(point.n) + width / 2) / width), rows);
int column = std::min(int((std::int64_t(point.k - 1) + width / 2) / width), row);
std::int64_t distance = std::abs(point.n - row * width) + std::int64_t(std::abs(point.k - 1 - column * width));
sum += std::min(std::int64_t(std::min(point.k - 1, (point.n + 1) / 2 - point.k)), distance);
}
return double(maximum) * maximum / (4 * double(width)) + double(points.size()) * sum / double(count);
};
if (estimate(alternative) < estimate(block)) block = alternative;
}
}
Online<Mint, Table> online(maximum, block);
for (const auto& point : points) store(point, online.query(point.n, point.k));
}
return answer;
}
}