競技プログラミング用 C++ ライブラリ
math/binomial_prefix_sum_light.hpp · GitHub で見る
64 KiBサイト · 軽量実装
この項目の軽量実装は light_combinatorics::BinomialPrefixSum です。以下の使用例・API一覧は共有ヘッダーの説明です。
\(\displaystyle S(n,k)=\sum_{i=0}^{k-1}\binom ni\) を素数 mod で計算します。右端を含みません。\(k\leq0\) なら 0、\(k\geq n+1\) なら \(2^n\) です。
全クエリがそろっている場合は light_combinatorics::binomial_prefix_sum で一括計算できます。クエリを 1 件ずつ渡す場合は light_combinatorics::BinomialPrefixSum を使います。
直前のクエリとの差分で更新する場合は 差分更新版 を使えます。
light_combinatorics 名前空間で使います。他の modint にも対応し、通常版と同時に include できます。
#include "lib/math/binomial_prefix_sum_light.hpp"
#include <cassert>
int main() {
light_combinatorics::BinomialPrefixSum<> sum(10000);
auto a = sum.query(5, 3); // 16
auto b = sum.prod(5, 1, 3); // 15
assert(a == mint(16) && b == mint(15));
}
一括計算では、入力順に対応する答えを返します。
#include "lib/math/binomial_prefix_sum_light.hpp"
#include <cassert>
#include <cstdint>
#include <utility>
#include <vector>
int main() {
std::vector<std::pair<int, std::int64_t>> queries{ {5, 3}, {5, 2}, {6, 2}, {5, 6}, {7, 4}, {5, 3} };
auto values = light_combinatorics::binomial_prefix_sum(queries);
assert((values == std::vector<mint>{16, 6, 7, 32, 64, 16}));
}
light_combinatorics::binomial_prefix_sum<Mint = mint, K = int>(queries, block_size = 0) は std::vector<Mint> を返します。queries は std::vector<std::pair<int, K>> で、各要素は {n,k} です。K は 64 bit 以下の符号付き整数型とし、n は非負で INT_MAX と法より小さくします。block_size は非負の int です。添字 i の返り値が queries[i] の答えになり、重複クエリもそのまま残します。空入力には空の列を返し、入力は変更しません。Mint の条件はクラス版と同じです。
一括計算は端や中央の直接答えられるクエリを先に処理し、残ったクエリに必要な範囲だけ階乗と格子を作ります。残ったクエリの n がすべて等しい場合は、その行の必要な prefix を一度だけ計算し、格子を作りません。
格子を使う場合、残ったクエリの最大 n を \(M\)、個数を \(R\) とすると、block_size == 0 では \(\displaystyle B_0=\max(1,\operatorname{round}(M/\sqrt R))\) を基準にします。クエリの標本から前計算と補正量を見積もり、\(B_0/2\leq B\leq B_0\) の範囲で幅を選びます。正の block_size は指定した幅を使います。直接答えられる場合と同じ行だけの場合は、指定幅にかかわらず格子を省きます。
| 操作 | 内容・返り値 | 計算量 |
|---|---|---|
light_combinatorics::BinomialPrefixSum<Mint>(max_n,block_size=0) |
0 <= n <= max_n のクエリを前計算。Mint の既定は mint、max_n の既定は 0 |
\(O(N+N^2/B)\) |
build(max_n,block_size=0) |
範囲とブロック幅を変更して前計算を作り直す。返り値は void
|
\(O(N+N^2/B)\) |
query(n,k) |
\(S(n,k)\) を Mint で返す。k は std::int64_t
|
\(O(B)\) |
prod(n,l,r) |
\(\displaystyle \sum_{i=l}^{r-1}\binom ni\) を Mint で返す。l・r は std::int64_t、l <= r が必要。空区間は 0、範囲外の添字の項も 0 |
\(O(B)\) |
max_n() |
前計算した \(n\) の上限を int で返す |
\(O(1)\) |
block_size() |
使用中のブロック幅を int で返す |
\(O(1)\) |
table_size() |
二次元格子に保存した Mint の個数を std::size_t で返す。階乗などの配列は含まない |
\(O(1)\) |
BinomialPrefixSum は \(n\) の上限を指定して構築します。query・prod はクエリを 1 個ずつ渡して使えます。呼び出し順序は自由で、const なオブジェクトからも取得できます。同じクエリも繰り返し呼び出せます。
max_n は非負で INT_MAX 未満、法より小さい必要があります。block_size は非負で、0 の場合は max_n + 1 の平方根を切り捨て、1 以上にした幅を使います。大きくすると前計算の保存量が減り、クエリ時に直接足す項が増えます。query・prod の n は前計算の範囲内で指定します。
Mint は素数 mod の modint です。mod()・inv()・整数からの構築・加減乗算に対応し、既定構築で 0 になる型を指定できます。使用中に法を変更しないでください。等値比較は不要です。既存の init_comb や COMB_MAX は使いません。
max_n を \(N\)、ブロック幅を \(B\)、クエリ数を \(Q\) とします。構築・build は \(O(N+N^2/B)\) 時間、\(O(N+N^2/B^2)\) 空間、query・prod は \(O(B)\)、その他のクエリは \(O(1)\) です。既定の幅では構築が \(\displaystyle O(N\sqrt N)\) 時間、空間が \(O(N)\)、クエリが \(\displaystyle O(\sqrt N)\) です。クエリ \(Q\) 個を処理する合計時間は \(O(N+N^2/B+QB)\) です。法上の演算を定数時間とし、前計算には定数回の逆元計算を含みます。
クエリ数 \(Q>0\) が事前に分かる場合、ブロック幅の目安は \(\displaystyle B=\max(1,\lfloor N/\sqrt Q\rfloor)\) です。クエリが少ないときは幅を大きくすることで前計算を軽くできます。次の例は独立したプログラムです。
#include "lib/math/binomial_prefix_sum_light.hpp"
#include <algorithm>
#include <cmath>
int main() {
const int max_n = 200000, queries = 64;
int block = std::max(1, int(max_n / std::sqrt(double(queries))));
light_combinatorics::BinomialPrefixSum<> sum(max_n, block);
auto value = sum.query(12345, 100);
(void)value;
}
格子と正規化は kemuniku の実装、一括処理は to-omer/competitive-library を参考にしています。
一括計算では、全クエリの最大 n を \(N\)、全クエリ数を \(Q\) として、時間は \(O(N+Q+M^2/B+RB)\)、空間は \(O(N+Q+M^2/B^2)\) です。幅の自動選択では \(O(N+Q+M\sqrt R)\) 時間、\(O(N+Q)\) 空間になります。格子を省く場合は \(O(N+Q)\) 時間・空間、空入力は \(O(1)\) 時間・空間です。
#ifndef BINOMIAL_PREFIX_SUM_LIGHT_HPP
#define BINOMIAL_PREFIX_SUM_LIGHT_HPP
#include "../components/binomial_prefix_core_light.hpp"
#include "../components/binomial_prefix_online.hpp"
#include "../components/binomial_prefix_batch.hpp"
namespace light_combinatorics {
template<class Mint = mint, class K = int>
std::vector<Mint> binomial_prefix_sum(const std::vector<std::pair<int, K>>& queries, int block_size = 0) {
return binomial_prefix_detail::batch<Mint, light_binomial_prefix_detail::Table<Mint>>(queries, block_size);
}
template<class Mint = mint> class BinomialPrefixSum : public binomial_prefix_detail::Online<Mint, light_binomial_prefix_detail::Table<Mint>> {
using Base = binomial_prefix_detail::Online<Mint, light_binomial_prefix_detail::Table<Mint>>;
public:
explicit BinomialPrefixSum(int max_n = 0, int block_size = 0) : Base(max_n, block_size) {}
};
}
#endif
#line 1 "math/binomial_prefix_sum_light.hpp"
#line 1 "components/binomial_prefix_core_light.hpp"
#line 1 "components/factorial_table_light.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 1 "components/factorial_table_generic.hpp"
#line 1 "components/barrett32.hpp"
#line 5 "components/barrett32.hpp"
#include <cstdint>
struct Barrett32 {
std::uint32_t mod;
std::uint64_t reciprocal;
explicit Barrett32(std::uint32_t m) : mod(m), reciprocal(m > 1 ? std::uint64_t((__uint128_t(1) << 64) / m) : 0) {
assert(m > 0);
}
std::uint32_t reduce(std::uint64_t x) const {
if (mod == 1) return 0;
std::uint64_t q = (__uint128_t(x) * reciprocal) >> 64, r = x - q * mod;
return r < mod ? r : r - mod;
}
std::uint32_t mul(std::uint32_t a, std::uint32_t b) const { return reduce(std::uint64_t(a) * b); }
std::uint32_t pow(std::uint32_t a, std::uint64_t e) const {
std::uint32_t r = mod == 1 ? 0 : 1;
for (; e; e >>= 1, a = mul(a, a)) if (e & 1) r = mul(r, a);
return r;
}
};
#line 5 "components/factorial_table_generic.hpp"
#include <concepts>
#line 7 "components/factorial_table_generic.hpp"
#include <limits>
#line 9 "components/factorial_table_generic.hpp"
namespace factorial_table_detail {
template<class Mint, bool Canonical = true> void build(Mint *factorial, Mint *inverse_factorial, int n) {
if constexpr (Canonical && requires(const Mint &x) { { x.val() } -> std::integral; }) {
const auto modulus = Mint::mod();
if (std::uint64_t(modulus) <= std::numeric_limits<std::uint32_t>::max()) {
Barrett32 arithmetic(modulus);
auto make = [](std::uint32_t value) { return Mint(value); };
std::uint32_t value = 1;
factorial[0] = make(value);
for (int i = 1; i <= n; ++i) {
value = arithmetic.mul(value, i);
factorial[i] = make(value);
}
inverse_factorial[n] = factorial[n].inv();
if constexpr (std::is_signed_v<decltype(inverse_factorial[n].val())>) {
std::int64_t residue = inverse_factorial[n].val() % std::int64_t(modulus);
value = residue < 0 ? residue + modulus : residue;
} else value = arithmetic.reduce(inverse_factorial[n].val());
for (int i = n; i > 0; --i) {
value = arithmetic.mul(value, i);
inverse_factorial[i - 1] = make(value);
}
return;
}
}
factorial[0] = Mint(1);
for (int i = 1; i <= n; ++i) factorial[i] = factorial[i - 1] * Mint(i);
inverse_factorial[n] = factorial[n].inv();
for (int i = n; i > 0; --i) inverse_factorial[i - 1] = inverse_factorial[i] * Mint(i);
}
}
#line 6 "components/factorial_table_light.hpp"
#include <algorithm>
#line 11 "components/factorial_table_light.hpp"
#include <vector>
namespace light_factorial_detail {
template<class Mint> struct Table {
decltype(Mint::mod()) modulus;
std::vector<Mint> factorial, inverse_factorial;
explicit Table(int max_n) : modulus(Mint::mod()) {
assert(0 <= max_n && max_n < std::numeric_limits<int>::max() && modulus > 1);
int n = static_cast<int>(std::min(std::uint64_t(max_n), std::uint64_t(modulus) - 1));
factorial.resize(n + 1), inverse_factorial.resize(n + 1);
factorial_table_detail::build<Mint, !std::is_same_v<Mint, mint>>(factorial.data(), inverse_factorial.data(), n);
}
};
}
#line 6 "components/binomial_prefix_core_light.hpp"
namespace light_binomial_prefix_detail {
template<class Mint> struct Table : light_factorial_detail::Table<Mint> {
using Base = light_factorial_detail::Table<Mint>;
using Base::factorial, Base::inverse_factorial;
std::vector<Mint> power2;
Mint half;
explicit Table(int n = 0) : Base(n) {
assert(std::uint64_t(n) < Mint::mod());
power2.resize(n + 1);
power2[0] = Mint(1);
for (int i = 1; i <= n; ++i) power2[i] = power2[i - 1] + power2[i - 1];
half = Mint((std::uint64_t(Mint::mod()) + 1) / 2);
}
void build(int n) { *this = Table(n); }
Mint binom(int n, int k) const {
if (k < 0 || k > n) return Mint(0);
return factorial[n] * inverse_factorial[k] * inverse_factorial[n - k];
}
Mint center(int n) const {
if (!n) return Mint(0);
return n & 1 ? power2[n - 1] : (power2[n] - binom(n, n / 2)) * half;
}
Mint range(int n, int l, int r) const {
assert(0 <= l && l <= r && r <= n + 1);
Mint sum(0);
for (int i = l; i < r; ++i) sum += inverse_factorial[i] * inverse_factorial[n - i];
return factorial[n] * sum;
}
};
}
#line 1 "components/binomial_prefix_online.hpp"
#line 1 "components/mint_inner_product.hpp"
#line 6 "components/mint_inner_product.hpp"
#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 7 "components/binomial_prefix_online.hpp"
#include <cmath>
#include <cstddef>
#line 12 "components/binomial_prefix_online.hpp"
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 1 "components/binomial_prefix_batch.hpp"
#line 6 "components/binomial_prefix_batch.hpp"
#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;
}
}
#line 6 "math/binomial_prefix_sum_light.hpp"
namespace light_combinatorics {
template<class Mint = mint, class K = int>
std::vector<Mint> binomial_prefix_sum(const std::vector<std::pair<int, K>>& queries, int block_size = 0) {
return binomial_prefix_detail::batch<Mint, light_binomial_prefix_detail::Table<Mint>>(queries, block_size);
}
template<class Mint = mint> class BinomialPrefixSum : public binomial_prefix_detail::Online<Mint, light_binomial_prefix_detail::Table<Mint>> {
using Base = binomial_prefix_detail::Online<Mint, light_binomial_prefix_detail::Table<Mint>>;
public:
explicit BinomialPrefixSum(int max_n = 0, int block_size = 0) : Base(max_n, block_size) {}
};
}