競技プログラミング用 C++ ライブラリ
components/polynomial_half_gcd_light.hpp · GitHub で見る
共通ページ · 軽量実装
fps_light_detail::PolynomialHalfGCD<T> は、軽量多項式 GCD が使う内部部品です。通常は公開する多項式 GCD のヘッダーを使います。F は LightFormalPowerSeries<T>、係数の法は 998244353 です。
| 型・関数 | 内容・返り値 |
|---|---|
Matrix |
成分 a を行優先で持つ 2×2 多項式行列。初期値は単位行列 |
Matrix::identity |
単位行列として処理を省略できるか |
high(a,k) |
定数項側から k 係数を除いた多項式。k >= 0
|
multiply(a,b) |
行列積 a*b
|
step(matrix,q) |
互除法の行列 [0,1;1,-q] を左から掛ける |
apply(matrix,a,b) |
行列を多項式ペアに作用させて上書きし、末尾の 0 を除く |
half(a,b) |
後ろの多項式の係数数を元の a.size()/2 以下にする変換行列 |
reduce(a,b,matrix = nullptr) |
b が零多項式になるまで縮小。matrix を渡すと同じ変換を左から累積する |
half は末尾の 0 を除いた多項式について a.size() > b.size() を前提とします。b は空でも構いません。作用後は先頭の多項式の係数数が元の半分より大きく、後ろは半分以下になります。行列式は ±1 です。reduce の入力も末尾の 0 を除きます。両方が空の入力も扱えます。
Matrix::a を直接書き換える場合は identity を正しく設定してください。NTT キャッシュや専用 SIMD 処理を持つ高速実装はこちら。
最大係数数を \(n\)、畳み込みの計算量を \(M(n)\) として、half・reduce は \(O(M(n)\log n)\)、行列積・行列の作用・step は \(O(M(n))\)、high は \(O(n)\) 時間です。追加空間は \(O(n)\) です。
#ifndef CP_COMPONENTS_POLYNOMIAL_HALF_GCD_LIGHT_HPP
#define CP_COMPONENTS_POLYNOMIAL_HALF_GCD_LIGHT_HPP
#include "../fps/fps_light.hpp"
#include <array>
namespace fps_light_detail {
template<class T>
struct PolynomialHalfGCD {
using F = LightFormalPowerSeries<T>;
struct Matrix {
std::array<F, 4> a{F{1}, F{}, F{}, F{1}};
bool identity = true;
};
static F high(const F &a, int k) {
assert(k >= 0);
return k >= int(a.size()) ? F{} : F(a.begin() + k, a.end());
}
static Matrix multiply(const Matrix &a, const Matrix &b) {
if (a.identity) return b;
if (b.identity) return a;
Matrix result;
result.identity = false;
for (int i = 0; i < 2; ++i) for (int j = 0; j < 2; ++j) {
result.a[2 * i + j] = a.a[2 * i] * b.a[j] + a.a[2 * i + 1] * b.a[2 + j];
result.a[2 * i + j].shrink();
}
return result;
}
static void step(Matrix &matrix, const F &q) {
F c = matrix.a[0] - q * matrix.a[2], d = matrix.a[1] - q * matrix.a[3];
c.shrink(), d.shrink();
matrix.a[0] = std::move(matrix.a[2]), matrix.a[1] = std::move(matrix.a[3]);
matrix.a[2] = std::move(c), matrix.a[3] = std::move(d);
matrix.identity = false;
}
static void apply(const Matrix &matrix, F &a, F &b) {
if (matrix.identity) return;
F c = matrix.a[0] * a + matrix.a[1] * b;
b = matrix.a[2] * a + matrix.a[3] * b, a = std::move(c);
a.shrink(), b.shrink();
}
static Matrix half(F a, F b) {
assert(a.size() > b.size());
const int m = int(a.size()) / 2;
Matrix result;
if (int(b.size()) <= m) return result;
if (a.size() <= 64) {
while (int(b.size()) > m) {
auto [q, r] = a.divmod(b);
step(result, q), a = std::move(b), b = std::move(r);
}
return result;
}
result = half(high(a, m), high(b, m));
apply(result, a, b);
if (int(b.size()) <= m) return result;
auto [q, r] = a.divmod(b);
step(result, q), a = std::move(b), b = std::move(r);
if (int(b.size()) <= m) return result;
const int k = 2 * m + 1 - int(a.size());
return multiply(half(high(a, k), high(b, k)), result);
}
static void reduce(F &a, F &b, Matrix *matrix = nullptr) {
while (!b.empty()) {
if (a.size() > b.size()) {
auto part = half(a, b);
apply(part, a, b);
if (matrix) *matrix = multiply(part, *matrix);
if (b.empty()) break;
}
auto [q, r] = a.divmod(b);
if (matrix) step(*matrix, q);
a = std::move(b), b = std::move(r);
}
}
};
}
#endif
#line 1 "components/polynomial_half_gcd_light.hpp"
#line 1 "fps/fps_light.hpp"
#line 1 "components/fps_light_backend.hpp"
#line 1 "components/fps_light_core.hpp"
#include <algorithm>
#include <bit>
#include <cassert>
#include <cstddef>
#include <initializer_list>
#include <limits>
#include <optional>
#include <type_traits>
#include <utility>
#include <vector>
#line 1 "math/mint.hpp"
#line 4 "math/mint.hpp"
#include <istream>
#include <ostream>
#line 7 "math/mint.hpp"
#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 15 "components/fps_light_core.hpp"
#line 1 "components/fps_light_ntt.hpp"
#line 11 "components/fps_light_ntt.hpp"
#if (defined(__x86_64__) || defined(__i386__)) && (defined(__GNUC__) || defined(__clang__))
#include <immintrin.h>
#endif
#if defined(__GNUC__) && !defined(__clang__) && (defined(__x86_64__) || defined(__i386__))
#pragma GCC push_options
#pragma GCC optimize("O3")
#endif
namespace fps_light_detail {
using std::size_t;
using Series = std::vector<mint>;
static_assert(sizeof(mint) == sizeof(unsigned) && std::is_trivially_copyable_v<mint>);
inline bool simd_available() {
#if (defined(__x86_64__) || defined(__i386__)) && (defined(__GNUC__) || defined(__clang__))
return __builtin_cpu_supports("avx2");
#else
return false;
#endif
}
template<bool Inverse>
void scalar_stage(mint *a, size_t n, size_t h, const mint *roots) {
for (size_t start = 0; start < n; start += h * 2)
for (size_t j = 0; j < h; ++j) {
mint x = a[start + j], y = a[start + j + h];
if constexpr (Inverse) y *= roots[j];
a[start + j] = x + y;
a[start + j + h] = x - y;
if constexpr (!Inverse) a[start + j + h] *= roots[j];
}
}
#if (defined(__x86_64__) || defined(__i386__)) && (defined(__GNUC__) || defined(__clang__))
__attribute__((target("avx2")))
inline __m256i product(__m256i x, __m256i y) {
const auto p = _mm256_set1_epi32(mint::mod());
auto even = _mm256_mul_epu32(x, y);
auto odd = _mm256_mul_epu32(_mm256_srli_epi64(x, 32), _mm256_srli_epi64(y, 32));
auto q = _mm256_mullo_epi32(_mm256_mullo_epi32(x, y), _mm256_set1_epi32(998244351));
even = _mm256_srli_epi64(_mm256_add_epi64(even, _mm256_mul_epu32(q, p)), 32);
odd = _mm256_add_epi64(odd, _mm256_mul_epu32(_mm256_srli_epi64(q, 32), p));
return _mm256_blend_epi32(even, odd, 0xaa);
}
__attribute__((target("avx2")))
inline __m256i fixed_product(__m256i x, __m256i w, __m256i q) {
auto even = _mm256_mul_epu32(x, q);
auto odd = _mm256_mul_epu32(_mm256_srli_epi64(x, 32), _mm256_srli_epi64(q, 32));
q = _mm256_blend_epi32(_mm256_srli_epi64(even, 32), odd, 0xaa);
return _mm256_sub_epi32(_mm256_mullo_epi32(x, w), _mm256_mullo_epi32(q, _mm256_set1_epi32(mint::mod())));
}
__attribute__((target("avx2")))
inline void pointwise_avx2(mint *a, const mint *b, size_t n) {
size_t i = 0;
for (; i + 8 <= n; i += 8) {
auto x = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(a + i));
auto y = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(b + i));
_mm256_storeu_si256(reinterpret_cast<__m256i *>(a + i), product(x, y));
}
for (; i < n; ++i) a[i] *= b[i];
}
__attribute__((target("avx2")))
inline void scale_avx2(mint *a, size_t n, mint factor) {
size_t i = 0;
unsigned value = factor.val();
const auto f = _mm256_set1_epi32(value);
const auto q = _mm256_set1_epi32((static_cast<unsigned long long>(value) << 32) / mint::mod());
for (; i + 8 <= n; i += 8) {
auto x = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(a + i));
_mm256_storeu_si256(reinterpret_cast<__m256i *>(a + i), fixed_product(x, f, q));
}
for (; i < n; ++i) a[i] *= factor;
}
__attribute__((target("avx2")))
inline __m256i sum_vec(__m256i x, __m256i y) {
auto z = _mm256_add_epi32(x, y);
return _mm256_min_epu32(z, _mm256_sub_epi32(z, _mm256_set1_epi32(2 * mint::mod())));
}
__attribute__((target("avx2")))
inline __m256i diff_vec(__m256i x, __m256i y) {
auto z = _mm256_sub_epi32(x, y);
return _mm256_min_epu32(z, _mm256_add_epi32(z, _mm256_set1_epi32(2 * mint::mod())));
}
template<int Half, bool Inverse>
__attribute__((target("avx2"), always_inline))
inline __m256i leaf_stage(__m256i x, const unsigned *w, const unsigned *q) {
constexpr int mask = Half == 4 ? 0xf0 : Half == 2 ? 0xcc : 0xaa;
__m256i factor{}, quotient{};
if constexpr (Half == 4) {
factor = _mm256_broadcastsi128_si256(_mm_loadu_si128(reinterpret_cast<const __m128i *>(w + 4)));
quotient = _mm256_broadcastsi128_si256(_mm_loadu_si128(reinterpret_cast<const __m128i *>(q + 4)));
}
if constexpr (Half == 2) {
factor = _mm256_broadcastq_epi64(_mm_loadl_epi64(reinterpret_cast<const __m128i *>(w + 2)));
quotient = _mm256_broadcastq_epi64(_mm_loadl_epi64(reinterpret_cast<const __m128i *>(q + 2)));
}
if constexpr (Inverse && Half > 1) x = _mm256_blend_epi32(x, fixed_product(x, factor, quotient), mask);
__m256i y;
if constexpr (Half == 4) y = _mm256_permute2x128_si256(x, x, 1);
if constexpr (Half == 2) y = _mm256_shuffle_epi32(x, 0x4e);
if constexpr (Half == 1) y = _mm256_shuffle_epi32(x, 0xb1);
auto high = diff_vec(y, x);
if constexpr (!Inverse && Half > 1) high = fixed_product(high, factor, quotient);
return _mm256_blend_epi32(sum_vec(x, y), high, mask);
}
template<bool Inverse>
__attribute__((target("avx2")))
void leaf8(mint *a, size_t n, const mint *roots) {
alignas(32) unsigned values[8], quotients[8];
for (int i = 0; i < 8; ++i) {
values[i] = roots[i].val();
quotients[i] = (static_cast<unsigned long long>(values[i]) << 32) / mint::mod();
}
for (size_t i = 0; i < n; i += 8) {
auto x = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(a + i));
x = leaf_stage<Inverse ? 1 : 4, Inverse>(x, values, quotients);
x = leaf_stage<2, Inverse>(x, values, quotients);
x = leaf_stage<Inverse ? 4 : 1, Inverse>(x, values, quotients);
_mm256_storeu_si256(reinterpret_cast<__m256i *>(a + i), x);
}
}
__attribute__((target("avx2")))
inline void expand_roots(mint *out, const mint *previous, size_t n, mint step) {
for (size_t i = 0; i < n; ++i) {
out[2 * i].v = previous[i].v;
out[2 * i + 1].v = mint::reduce(static_cast<unsigned long long>(previous[i].v) * step.v);
}
}
template<bool Inverse>
__attribute__((target("avx2")))
void vector_stage(mint *a, size_t n, size_t h, const mint *roots) {
for (size_t start = 0; start < n; start += h * 2)
for (size_t j = 0; j < h; j += 8) {
auto *first = reinterpret_cast<__m256i *>(a + start + j);
auto *last = reinterpret_cast<__m256i *>(a + start + j + h);
auto x = _mm256_loadu_si256(first), y = _mm256_loadu_si256(last);
auto w = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(roots + j));
if constexpr (Inverse) y = product(y, w);
auto sum = sum_vec(x, y), difference = diff_vec(x, y);
if constexpr (!Inverse) difference = product(difference, w);
_mm256_storeu_si256(first, sum);
_mm256_storeu_si256(last, difference);
}
}
#endif
struct Roots {
Series forward{0, 1}, inverse{0, 1};
void prepare(size_t n) {
size_t begin = forward.size();
if (begin >= n) return;
forward.resize(n), inverse.resize(n);
for (size_t h = begin; h < n; h *= 2) {
mint step = mint(3).pow((mint::mod() - 1) / (h * 2));
#if (defined(__x86_64__) || defined(__i386__)) && (defined(__GNUC__) || defined(__clang__))
if (simd_available() && h >= 16) expand_roots(forward.data() + h, forward.data() + h / 2, h / 2, step);
else
#endif
for (size_t j = 0; j < h / 2; ++j) {
forward[h + 2 * j] = forward[h / 2 + j];
forward[h + 2 * j + 1] = forward[h / 2 + j] * step;
}
inverse[h] = 1;
for (size_t j = 1; j < h; ++j) inverse[h + j] = -forward[2 * h - j];
}
}
};
inline void pointwise(Series &a, const Series &b, [[maybe_unused]] bool vectorized = simd_available()) {
assert(a.size() <= b.size());
#if (defined(__x86_64__) || defined(__i386__)) && (defined(__GNUC__) || defined(__clang__))
if (vectorized) return pointwise_avx2(a.data(), b.data(), a.size());
#endif
for (size_t i = 0; i < a.size(); ++i) a[i] *= b[i];
}
inline void scale(Series &a, mint factor, [[maybe_unused]] bool vectorized = simd_available()) {
#if (defined(__x86_64__) || defined(__i386__)) && (defined(__GNUC__) || defined(__clang__))
if (vectorized) return scale_avx2(a.data(), a.size(), factor);
#endif
for (auto &x : a) x *= factor;
}
template<bool Inverse>
void stage(mint *a, size_t n, size_t h, const mint *roots, [[maybe_unused]] bool vectorized) {
#if (defined(__x86_64__) || defined(__i386__)) && (defined(__GNUC__) || defined(__clang__))
if (vectorized && h >= 8) return vector_stage<Inverse>(a, n, h, roots);
#endif
scalar_stage<Inverse>(a, n, h, roots);
}
inline void transform(Series &a, bool inverse = false) {
size_t n = a.size();
assert(std::has_single_bit(n) && n <= (size_t(1) << 23));
static Roots roots;
roots.prepare(n);
bool vectorized = simd_available();
if (inverse) {
size_t h = 1;
#if (defined(__x86_64__) || defined(__i386__)) && (defined(__GNUC__) || defined(__clang__))
if (vectorized && n >= 8) leaf8<true>(a.data(), n, roots.inverse.data()), h = 8;
#endif
for (; h < n; h *= 2) stage<true>(a.data(), n, h, roots.inverse.data() + h, vectorized);
scale(a, mint::raw(mint::mod() - (mint::mod() - 1) / n), vectorized);
} else {
size_t h = n / 2;
for (; h >= 8; h /= 2) stage<false>(a.data(), n, h, roots.forward.data() + h, vectorized);
#if (defined(__x86_64__) || defined(__i386__)) && (defined(__GNUC__) || defined(__clang__))
if (vectorized && n >= 8) return leaf8<false>(a.data(), n, roots.forward.data());
#endif
for (; h; h /= 2) stage<false>(a.data(), n, h, roots.forward.data() + h, vectorized);
}
}
inline Series multiply(const Series &a, const Series &b) {
if (a.empty() || b.empty()) return {};
[[maybe_unused]] constexpr size_t limit = size_t(1) << 23;
assert(a.size() <= limit && b.size() <= limit && a.size() + b.size() - 1 <= limit);
size_t length = a.size() + b.size() - 1;
if (std::min(a.size(), b.size()) <= 32) {
Series result(length);
for (size_t i = 0; i < a.size(); ++i)
for (size_t j = 0; j < b.size(); ++j) result[i + j] += a[i] * b[j];
return result;
}
size_t n = std::bit_ceil(length);
Series x = a;
x.resize(n);
transform(x);
if (&a == &b) {
pointwise(x, x);
} else {
Series y = b;
y.resize(n);
transform(y);
pointwise(x, y);
}
transform(x, true);
x.resize(length);
return x;
}
}
#if defined(__GNUC__) && !defined(__clang__) && (defined(__x86_64__) || defined(__i386__))
#pragma GCC pop_options
#endif
#line 1 "components/fps_sparse_scalar.hpp"
#line 1 "components/fps_sparse_terms.hpp"
#line 7 "components/fps_sparse_terms.hpp"
#include <numeric>
#line 10 "components/fps_sparse_terms.hpp"
namespace fps_detail {
struct SparseTerms {
using Series = std::vector<mint>;
using Terms = std::vector<std::pair<int, mint>>;
enum class Kind { inverse, logarithm, exponential, power };
static Terms collect(const mint *f, int size, int n, int limit = 12) {
Terms terms;
for (int i = 1; i < std::min(size, n) && int(terms.size()) <= limit; ++i)
if (f[i] != mint(0)) terms.emplace_back(i, f[i]);
return terms;
}
};
}
#line 5 "components/fps_sparse_scalar.hpp"
namespace fps_detail {
struct ScalarSparseSeries : SparseTerms {
template <Kind kind>
static Series get(Terms terms, int n, mint initial, mint exponent, const mint *(*inverses)(int)) {
assert(n >= 0 && terms.size() <= 64 && (kind == Kind::inverse || unsigned(n) < mint::mod()));
if (!n) return {};
Series out(n);
out[0] = initial;
if (terms.empty()) return out;
if constexpr (kind == Kind::power) {
unsigned e = exponent.val();
if (e <= 4 && terms.size() <= 12 && (n >= 4096 || terms.size() <= 3)) {
Terms power{{0, initial}};
for (unsigned k = 0; k < e; ++k) {
const auto previous = power;
for (auto [i, x] : previous) for (auto [j, y] : terms)
if (i < n - j) power.emplace_back(i + j, x * y);
}
out[0] = 0;
for (auto [i, x] : power) out[i] += x;
return out;
}
}
int stride = 0;
for (auto [j, x] : terms) stride = std::gcd(stride, j);
const int count = (n - 1) / stride + 1;
for (auto &[j, x] : terms) j /= stride;
if constexpr (kind == Kind::exponential) for (auto &[j, x] : terms) x *= mint(j);
const mint *inv = kind == Kind::inverse ? nullptr : inverses(count);
if (terms.size() == 1) {
mint value = kind == Kind::logarithm ? mint(-1) : initial;
mint ratio = kind == Kind::exponential ? terms[0].second : -terms[0].second;
for (int i = 1; i < count; ++i) {
value *= ratio;
if constexpr (kind == Kind::power) value *= (mint(i - 1) - exponent) * inv[i];
if constexpr (kind == Kind::exponential) value *= inv[i];
out[i * stride] = value;
}
} else for (int i = terms[0].first; i < count; ++i) {
mint sum = 0;
for (auto [j, x] : terms) {
if (j > i) break;
if constexpr (kind == Kind::power) x *= (exponent + mint(1)) * mint(j) - mint(i);
if constexpr (kind == Kind::inverse || kind == Kind::logarithm) x = -x;
sum += x * out[(i - j) * stride];
if constexpr (kind == Kind::logarithm) if (j == i) sum -= mint(j) * x;
}
if constexpr (kind == Kind::power || kind == Kind::exponential) sum *= inv[i];
out[i * stride] = sum;
}
if constexpr (kind == Kind::logarithm) for (int i = 1; i < count; ++i) out[i * stride] *= inv[i];
return out;
}
};
}
#line 18 "components/fps_light_core.hpp"
#if defined(__GNUC__) && !defined(__clang__) && (defined(__x86_64__) || defined(__i386__))
#pragma GCC push_options
#pragma GCC optimize("O3")
#endif
template<class T = mint>
struct [[nodiscard, gnu::warn_unused]] LightFormalPowerSeries : std::vector<T> {
static_assert(T::mod() == 998244353);
using base = std::vector<T>;
using F = LightFormalPowerSeries;
using Sparse = fps_detail::ScalarSparseSeries;
using Kind = Sparse::Kind;
using Size = std::size_t;
using base::base;
LightFormalPowerSeries() = default;
LightFormalPowerSeries(base a) : base(std::move(a)) {}
F &operator=(base a) { base::operator=(std::move(a)); return *this; }
F &operator=(std::initializer_list<T> a) { base::operator=(a); return *this; }
static F multiply(const F &a, const F &b) {
if constexpr (std::is_same_v<T, mint>) return fps_light_detail::multiply(a, b);
else {
auto x = a.native();
return from_native(&a == &b ? Native::multiply(x, x) : Native::multiply(x, b.native()));
}
}
F operator-() const { F r = *this; for (T &x : r) x = -x; return r; }
F &operator+=(const F &g) {
if (this->size() < g.size()) this->resize(g.size());
for (Size i = 0; i < g.size(); ++i) (*this)[i] += g[i];
return *this;
}
F &operator-=(const F &g) {
if (this->size() < g.size()) this->resize(g.size());
for (Size i = 0; i < g.size(); ++i) (*this)[i] -= g[i];
return *this;
}
F &operator+=(T x) { if (this->empty()) this->resize(1); (*this)[0] += x; return *this; }
F &operator-=(T x) { return *this += -x; }
F &operator*=(const F &g) { return *this = multiply(*this, g); }
F &operator/=(const F &g) { return *this = div_series(g); }
F &operator%=(const F &g) { return *this = divmod(g).second; }
F &operator*=(T x) { for (T &v : *this) v *= x; return *this; }
F &operator/=(T x) { return *this *= x.inv(); }
friend F operator+(F a, const F &b) { return a += b; }
friend F operator-(F a, const F &b) { return a -= b; }
friend F operator*(const F &a, const F &b) { return multiply(a, b); }
friend F operator/(const F &a, const F &b) { return a.div_series(b); }
friend F operator%(const F &a, const F &b) { return a.divmod(b).second; }
friend F operator+(F a, T b) { return a += b; }
friend F operator-(F a, T b) { return a -= b; }
friend F operator*(F a, T b) { return a *= b; }
friend F operator/(F a, T b) { return a /= b; }
friend F operator+(T a, F b) { return b += a; }
friend F operator-(T a, const F &b) { return -b + a; }
friend F operator*(T a, F b) { return b *= a; }
F &operator<<=(int d) {
assert(d >= 0);
auto edge = std::shift_right(this->begin(), this->end(), d);
std::fill(this->begin(), edge, T(0));
return *this;
}
F &operator>>=(int d) {
assert(d >= 0);
auto edge = std::shift_left(this->begin(), this->end(), d);
std::fill(edge, this->end(), T(0));
return *this;
}
friend F operator<<(F a, int d) { return a <<= d; }
friend F operator>>(F a, int d) { return a >>= d; }
F pre(int n) const {
assert(n >= 0);
return F(this->begin(), this->begin() + std::min(Size(n), this->size()));
}
void shrink() { while (!this->empty() && this->back() == T(0)) this->pop_back(); }
void trim() { shrink(); }
[[nodiscard]] T eval(T x) const {
T res = 0;
for (auto it = this->rbegin(); it != this->rend(); ++it) res = res * x + *it;
return res;
}
F diff() const {
F res(this->size());
for (Size i = 1; i < this->size(); ++i) res[i - 1] = (*this)[i] * T(i);
return res;
}
F integral() const {
assert(this->size() < T::mod());
F res(this->size());
const auto &inv = inverses(static_cast<int>(this->size()));
for (Size i = 1; i < this->size(); ++i) res[i] = (*this)[i - 1] * inv[i];
return res;
}
void onemul(int d, T c) {
assert(d >= 0);
if (!d) { *this *= T(1) + c; return; }
for (Size i = this->size(); i > Size(d); --i) (*this)[i - 1] += (*this)[i - 1 - d] * c;
}
void onediv(int d, T c) {
assert(d >= 0);
if (!d) { *this /= T(1) + c; return; }
for (Size i = d; i < this->size(); ++i) (*this)[i] -= (*this)[i - d] * c;
}
F inv(int deg = -1) const {
const int n = precision(deg);
if (!n) return {};
assert(!this->empty() && (*this)[0] != T(0));
if constexpr (!std::is_same_v<T, mint>) return from_native(native().inv(n));
else {
if (auto r = sparse<Kind::inverse>(0, n, 32, 12, T(0)); !r.empty()) return r;
const int work = newton_precision(n), initial = 32;
F res = pre(initial).inv(initial);
const T recip = res[0];
for (int k = initial; k < work; k *= 2) inverse_step(*this, res, std::min(k * 2, work));
res.resize(n);
for (int i = work; i < n; ++i) res[i] = -dot(*this, res, i) * recip;
return res;
}
}
F div_series(const F &g, int deg = -1) const {
const int n = precision(deg);
if (!n) return {};
assert(!g.empty() && g[0] != T(0));
if constexpr (!std::is_same_v<T, mint>) return from_native(native().div_series(g.native(), n));
else {
if (n <= 32) {
F res(n);
const T recip = g[0].inv();
for (int i = 0; i < n; ++i) {
res[i] = (coefficient(i) - dot(g, res, i)) * recip;
}
return res;
}
const int work = newton_precision(n), k = std::bit_floor(unsigned(work - 1)), length = k * 2;
F recip = g.inv(k), res = pre(k);
const T constant = recip[0];
recip.resize(length), res.resize(length);
transform(recip);
inverse_product(res, recip);
zero(res, k);
F quotient = res, error = g.pre(work);
error.resize(length);
transform(quotient);
inverse_product(error, quotient);
zero(error, 0, k);
for (int i = k; i < work; ++i) error[i] = coefficient(i) - error[i];
zero(error, work);
inverse_product(error, recip);
res.resize(n);
std::copy(error.begin() + k, error.begin() + work, res.begin() + k);
for (int i = work; i < n; ++i) {
res[i] = (coefficient(i) - dot(g, res, i)) * constant;
}
return res;
}
}
F log(int deg = -1) const {
const int n = precision(deg);
if (!n) return {};
assert(!this->empty() && (*this)[0] == T(1));
if constexpr (!std::is_same_v<T, mint>) return from_native(native().log(n));
else {
if (n >= 256) if (auto r = sparse<Kind::logarithm>(0, n, 12, 0, T(0)); !r.empty()) return r;
F res = pre(n).diff().div_series(*this, n - 1);
res.resize(n);
return res.integral();
}
}
F exp(int deg = -1) const {
const int n = precision(deg);
if (!n) return {};
assert(this->empty() || (*this)[0] == T(0));
if constexpr (!std::is_same_v<T, mint>) return from_native(native().exp(n));
else {
if (auto r = sparse<Kind::exponential>(0, n, 64, 24, T(1)); !r.empty()) return r;
const int work = newton_precision(n), initial = 32;
const auto &inv = inverses(n);
F res = pre(initial).exp(initial);
F reciprocal = res.inv(), recip;
for (int k = initial; k < work; k *= 2) {
const int length = k * 2, m = std::min(length, work);
F freq = res;
freq.resize(length);
transform(freq);
if (k > initial) inverse_update(freq.pre(k), recip, reciprocal, k);
F error(k);
for (int i = 1; i < k && Size(i) < this->size(); ++i) error[i - 1] = (*this)[i] * T(i);
inverse_product(error, freq);
const T last = -error.back();
for (int i = k - 1; i; --i) error[i] = res[i] * T(i) - error[i - 1];
error[0] = last;
error.resize(length);
recip = reciprocal;
recip.resize(length);
transform(recip);
inverse_product(error, recip);
for (int i = 0; i < m - k; ++i) error[i] = coefficient(k + i) - error[i] * inv[k + i];
zero(error, m - k);
inverse_product(error, freq);
res.resize(m);
std::copy_n(error.begin(), m - k, res.begin() + k);
}
res.resize(n);
for (int i = work; i < n; ++i) res[i] = dot<true>(*this, res, i) * inv[i];
return res;
}
}
F sqrt(int deg = -1) const {
const int n = precision(deg);
if (!n) return {};
if constexpr (!std::is_same_v<T, mint>) return from_native(native().sqrt(n));
else {
int first = 0, last = static_cast<int>(std::min(this->size(), Size(n)));
while (first < last && (*this)[first] == T(0)) ++first;
if (first == last) return F(n);
if (first & 1) return {};
auto constant = scalar_sqrt((*this)[first]);
if (!constant) return {};
const int shift = first / 2, target = n - shift, work = newton_precision(target), initial = std::min(work, 32);
if (target >= 256) {
F r = sparse<Kind::power>(first, target, 4, 0, *constant, T(2).inv());
if (!r.empty()) { r.insert(r.begin(), shift, T(0)); return r; }
}
F f(this->begin() + first, this->begin() + last);
const T half = T(2).inv();
F res = f.template sparse<Kind::power>(0, initial, 32, 0, *constant, half);
if (initial == target) {
res.insert(res.begin(), shift, T(0));
return res;
}
F reciprocal = res.inv();
for (int k = initial; k < work; k *= 2) {
const int length = k * 2, m = std::min(length, work);
F error;
if (k > initial) inverse_step(res, reciprocal, k, &error);
if (error.empty()) { error = res; transform(error); }
F recip = reciprocal;
pointwise(error, error);
transform(error, true);
error.resize(length), recip.resize(length);
for (int i = 0; i < m - k; ++i) {
T low = f.coefficient(i);
T high = f.coefficient(k + i);
error[i] = (low + high - error[i]) * half;
}
zero(error, m - k);
transform(error);
transform(recip);
pointwise(error, recip);
transform(error, true);
res.resize(m);
std::copy_n(error.begin(), m - k, res.begin() + k);
}
res.resize(target);
for (int i = work; i < target; ++i)
res[i] = (f.coefficient(i) - dot(res, res, i, i - 1)) * reciprocal[0] * half;
res.insert(res.begin(), shift, T(0));
return res;
}
}
F pow(long long k) const {
assert(k >= 0);
const int n = precision(-1);
if (!n) return {};
if (!k) { F res(n); res[0] = 1; return res; }
if (k == 1) return *this;
int first = 0;
while (first < n && (*this)[first] == T(0)) ++first;
if (first == n || first > (n - 1) / k) return F(n);
const int shift = static_cast<int>(first * k), len = n - shift;
const T scale = (*this)[first].pow(k);
if constexpr (std::is_same_v<T, mint>) {
F r = sparse<Kind::power>(first, len, 12, 0, scale, T(k));
if (!r.empty()) { r.insert(r.begin(), shift, T(0)); return r; }
}
if (k <= 4 && first == 0) {
F square = multiply(*this, *this).pre(n);
return k == 2 ? square : multiply(square, k == 3 ? *this : square).pre(n);
}
F f(this->begin() + first, this->end());
f.resize(len);
f /= f[0];
F res = (f.log(len) * T(k)).exp(len) * scale;
res.insert(res.begin(), shift, T(0));
return res;
}
[[nodiscard]] std::pair<F, F> divmod(const F &divisor) const {
F a = *this, b = divisor;
a.shrink(), b.shrink();
assert(!b.empty());
if (a.size() < b.size()) return {{}, std::move(a)};
assert(a.size() <= (Size(1) << 22));
const int qn = static_cast<int>(a.size() - b.size() + 1), m = static_cast<int>(b.size());
if (m <= 32 || Size(m) * qn <= 2048) {
F q(qn);
const T recip = b.back().inv();
for (int i = qn - 1; i >= 0; --i) {
T value = q[i] = a[i + m - 1] * recip;
for (int j = 0; j < m; ++j) a[i + j] -= value * b[j];
}
a.resize(m - 1);
a.shrink(), q.shrink();
return {std::move(q), std::move(a)};
}
F ra(a.rbegin(), a.rend()), rb(b.rbegin(), b.rend());
F q = ra.div_series(rb, qn);
std::reverse(q.begin(), q.end());
F r = a - multiply(q, b);
r.resize(m - 1);
r.shrink(), q.shrink();
return {std::move(q), std::move(r)};
}
private:
template <bool Weighted = false>
static T dot(const F &a, const F &b, int i, int last = -1) {
T sum = 0;
if (last < 0) last = i;
for (int j = 1; j <= last && Size(j) < a.size(); ++j) {
T value = a[j] * b[i - j];
if constexpr (Weighted) value *= T(j);
sum += value;
}
return sum;
}
T coefficient(int i) const { return Size(i) < this->size() ? (*this)[i] : T(0); }
static void inverse_product(F &a, const F &freq) {
transform(a); pointwise(a, freq); transform(a, true);
}
static void zero(F &a, int first, int last = -1) {
std::fill(a.begin() + first, last < 0 ? a.end() : a.begin() + last, T(0));
}
static void transform(F &a, bool recip = false) { fps_light_detail::transform(a, recip); }
static void pointwise(F &a, const F &b) { fps_light_detail::pointwise(a, b); }
template <Kind kind>
F sparse(int first, int n, int limit, int budget, T initial, T exponent = T(0)) const {
auto terms = Sparse::collect(this->data() + first, int(this->size()) - first, n, limit);
int stride = 0;
for (auto [j, x] : terms) stride = std::gcd(stride, j);
if (int(terms.size()) > limit || (budget && n > 256 && terms.size() > Size(budget * stride))) return {};
if constexpr (kind == Kind::inverse || kind == Kind::power) {
T recip = (*this)[first].inv();
for (auto &[j, x] : terms) x *= recip;
if constexpr (kind == Kind::inverse) initial = recip;
}
return F(Sparse::get<kind>(std::move(terms), n, initial, exponent, inverse_table));
}
static int newton_precision(int n) {
const int prefix = std::bit_floor(unsigned(n));
return prefix >= 4096 && n - prefix <= 4 ? prefix : n;
}
using Native = LightFormalPowerSeries<mint>;
Native native() const {
Native res(this->size());
for (Size i = 0; i < this->size(); ++i) res[i] = mint::raw((*this)[i].val());
return res;
}
static F from_native(const Native &a) {
F res(a.size());
for (Size i = 0; i < a.size(); ++i) res[i] = T::raw(a[i].val());
return res;
}
static void inverse_step(const F &f, F &res, int n, F *freq = nullptr) {
const int k = static_cast<int>(res.size()), length = k * 2;
assert(k > 0 && std::has_single_bit(unsigned(k)) && k < n && n <= length);
F error = f.pre(n), recip = res;
error.resize(length), recip.resize(length);
transform(error);
if (freq) *freq = error;
transform(recip);
inverse_update(std::move(error), recip, res, n);
}
static void inverse_update(F error, const F &recip, F &res, int n) {
const int k = static_cast<int>(res.size());
pointwise(error, recip);
transform(error, true);
zero(error, 0, k);
inverse_product(error, recip);
res.resize(n);
for (int i = k; i < n; ++i) res[i] = -error[i];
}
int precision(int deg) const {
assert(this->size() <= Size(std::numeric_limits<int>::max()));
int n = deg < 0 ? static_cast<int>(this->size()) : deg;
assert(0 <= n && n <= (1 << 22));
return n;
}
static const mint *inverse_table(int n) {
if constexpr (std::is_same_v<T, mint>) return inverses(n).data();
else return nullptr;
}
static const std::vector<T> &inverses(int n) {
static std::vector<T> inv{T(0), T(1)};
for (int i = static_cast<int>(inv.size()); i < n; ++i) inv.push_back(T::raw(T::mod() - T::mod() / i) * inv[T::mod() % i]);
return inv;
}
static std::optional<T> scalar_sqrt(T a) {
if (a == T(1)) return a;
constexpr int q = 119;
T t = a.pow((q - 1) / 2), x = t * a, c = T(3).pow(q);
t *= x;
int m = 23;
while (t != T(1)) {
int i = 0;
for (T next = t; next != T(1) && i < m; next *= next) ++i;
if (i == m) return std::nullopt;
T b = c.pow(1LL << (m - i - 1));
x *= b;
c = b * b;
t *= c;
m = i;
}
return T::raw(std::min(x.val(), T::mod() - x.val()));
}
};
using light_fps = LightFormalPowerSeries<>;
using LightFPS = light_fps;
#if defined(__GNUC__) && !defined(__clang__) && (defined(__x86_64__) || defined(__i386__))
#pragma GCC pop_options
#endif
#line 1 "components/polynomial_product_tree.hpp"
#line 1 "components/polynomial_multiply.hpp"
#line 6 "components/polynomial_multiply.hpp"
namespace polynomial_detail {
template<class F, std::size_t Limit = 1U << 23>
struct Products {
static_assert(Limit >= 2);
static F multiply(const F& a, const F& b) {
if (a.empty() || b.empty()) return {};
const auto n = a.size() + b.size() - 1;
return n <= Limit ? F::multiply(a, b) : multiply_low(a, b, n);
}
static F multiply_low(const F& a, const F& b, std::size_t n) {
F result(n);
constexpr std::size_t block = Limit / 2;
for (std::size_t i = 0; i < std::min(a.size(), n); i += block)
for (std::size_t j = 0; j < std::min(b.size(), n - i); j += block) {
F x(a.begin() + i, a.begin() + std::min(i + block, a.size()));
F y(b.begin() + j, b.begin() + std::min(j + block, b.size()));
F p = F::multiply(x, y);
for (std::size_t k = 0; k < std::min(p.size(), n - i - j); ++k)
result[i + j + k] += p[k];
}
return result;
}
};
}
#line 10 "components/polynomial_product_tree.hpp"
namespace polynomial_detail {
template<class F, std::size_t Limit = 1U << 23>
struct ProductTree : Products<F, Limit> {
using Products<F, Limit>::multiply;
using Products<F, Limit>::multiply_low;
static_assert(Limit >= 2);
using T = typename F::value_type;
std::size_t size = 1, count;
std::vector<F> product;
static void trim(F& f) { while (!f.empty() && f.back() == T(0)) f.pop_back(); }
explicit ProductTree(const std::vector<T>& xs) : count(xs.size()) {
assert(count <= Limit);
while (size < count) size <<= 1;
product.resize(2 * size);
for (std::size_t i = 0; i < size; ++i)
product[size + i] = i < count ? F{-xs[i], T(1)} : F{T(1)};
for (std::size_t i = size; --i;) {
const auto& a = product[2 * i];
const auto& b = product[2 * i + 1];
product[i] = b.size() == 1 ? a : multiply(a, b);
}
}
std::vector<T> evaluate(const F& f) const {
std::vector<T> result(count);
if (!count) return result;
auto remainder = [](const F& a, const F& b) {
return a.size() < b.size() ? a : a.divmod(b).second;
};
auto visit = [&](auto&& self, std::size_t node, const F& value) -> void {
if (value.empty()) return;
if (node >= size) {
result[node - size] = value[0];
return;
}
self(self, node * 2, remainder(value, product[node * 2]));
if (product[node * 2 + 1].size() > 1)
self(self, node * 2 + 1, remainder(value, product[node * 2 + 1]));
};
F value = f;
trim(value);
visit(visit, 1, remainder(value, product[1]));
return result;
}
F interpolate(const std::vector<T>& ys) const {
assert(ys.size() == count);
if (!count) return {};
F derivative = product[1].diff();
trim(derivative);
std::vector<T> denominator = evaluate(derivative), prefix(count + 1, T(1));
for (std::size_t i = 0; i < count; ++i) {
assert(denominator[i] != T(0));
prefix[i + 1] = prefix[i] * denominator[i];
}
T inverse = prefix[count].inv();
std::vector<F> value(2 * size);
for (std::size_t i = count; i--;) {
value[size + i] = F{ys[i] * inverse * prefix[i]};
inverse *= denominator[i];
}
for (std::size_t i = size; --i;) {
value[i] = F::multiply(value[2 * i], product[2 * i + 1])
+ F::multiply(value[2 * i + 1], product[2 * i]);
value[2 * i] = F();
value[2 * i + 1] = F();
}
value[1].resize(count);
return std::move(value[1]);
}
};
}
#line 6 "components/fps_light_backend.hpp"
namespace fps_light_detail {
template<class T>
using ProductTree = polynomial_detail::ProductTree<LightFormalPowerSeries<T>>;
}
namespace light_fps_algorithms {
template<class T>
LightFormalPowerSeries<T> taylor_shift(LightFormalPowerSeries<T> f, std::type_identity_t<T> c) {
using F = LightFormalPowerSeries<T>;
const std::size_t n = f.size();
if (n <= 1 || c == T(0)) return f;
assert(n < T::mod());
F a(n), b(n);
T factorial = 1;
a[n - 1] = f[0];
for (std::size_t i = 1; i < n; ++i) {
factorial *= T(i);
a[n - 1 - i] = f[i] * factorial;
}
f[n - 1] = factorial.inv();
for (std::size_t i = n - 1; i; --i) f[i - 1] = f[i] * T(i);
T power = 1;
for (std::size_t i = 0; i < n; ++i) {
b[i] = power * f[i];
power *= c;
}
a = F::multiply(a, b);
for (std::size_t i = 0; i < n; ++i) f[i] *= a[n - 1 - i];
return f;
}
template<class T>
T bostan_mori(LightFormalPowerSeries<T> p, LightFormalPowerSeries<T> q, long long k) {
using F = LightFormalPowerSeries<T>;
assert(k >= 0 && !q.empty() && q[0] != T(0));
p.shrink();
q.shrink();
while (k && !p.empty() && q.size() > 1) {
F even((q.size() + 1) / 2), odd(q.size() / 2);
for (std::size_t i = 0; i < q.size(); ++i) (i & 1 ? odd : even)[i / 2] = q[i];
for (std::size_t i = 1; i < q.size(); i += 2) q[i] = -q[i];
F numerator = F::multiply(p, q);
q = F::multiply(even, even), odd = F::multiply(odd, odd);
p.resize((numerator.size() + 1 - std::size_t(k & 1)) / 2);
for (std::size_t i = 0; i < p.size(); ++i) p[i] = numerator[2 * i + std::size_t(k & 1)];
if (q.size() == odd.size()) q.push_back(T(0));
for (std::size_t i = 0; i < odd.size(); ++i) q[i + 1] -= odd[i];
p.shrink();
k >>= 1;
}
return static_cast<unsigned long long>(k) < p.size() ? p[std::size_t(k)] / q[0] : T(0);
}
template<class T>
T linear_recurrence(const std::vector<T> &initial, const std::vector<T> &coefficients, long long k) {
using F = LightFormalPowerSeries<T>;
assert(k >= 0 && initial.size() == coefficients.size());
const std::size_t n = initial.size();
if (static_cast<unsigned long long>(k) < n) return initial[std::size_t(k)];
if (!n) return T(0);
F q(n + 1);
q[0] = T(1);
for (std::size_t i = 0; i < n; ++i) q[i + 1] = -coefficients[i];
F p = F::multiply(F(initial.begin(), initial.end()), q).pre(static_cast<int>(n));
return bostan_mori(std::move(p), std::move(q), k);
}
template<class T>
std::vector<T> multipoint_eval(const LightFormalPowerSeries<T> &f, const std::vector<T> &xs) {
if (f.size() <= 32 || xs.size() <= 32) {
std::vector<T> result;
result.reserve(xs.size());
for (T x : xs) result.push_back(f.eval(x));
return result;
}
return fps_light_detail::ProductTree<T>(xs).evaluate(f);
}
template<class T>
LightFormalPowerSeries<T> interpolate(const std::vector<T> &xs, const std::vector<T> &ys) {
assert(xs.size() == ys.size());
return fps_light_detail::ProductTree<T>(xs).interpolate(ys);
}
}
#line 4 "fps/fps_light.hpp"
#line 5 "components/polynomial_half_gcd_light.hpp"
#include <array>
namespace fps_light_detail {
template<class T>
struct PolynomialHalfGCD {
using F = LightFormalPowerSeries<T>;
struct Matrix {
std::array<F, 4> a{F{1}, F{}, F{}, F{1}};
bool identity = true;
};
static F high(const F &a, int k) {
assert(k >= 0);
return k >= int(a.size()) ? F{} : F(a.begin() + k, a.end());
}
static Matrix multiply(const Matrix &a, const Matrix &b) {
if (a.identity) return b;
if (b.identity) return a;
Matrix result;
result.identity = false;
for (int i = 0; i < 2; ++i) for (int j = 0; j < 2; ++j) {
result.a[2 * i + j] = a.a[2 * i] * b.a[j] + a.a[2 * i + 1] * b.a[2 + j];
result.a[2 * i + j].shrink();
}
return result;
}
static void step(Matrix &matrix, const F &q) {
F c = matrix.a[0] - q * matrix.a[2], d = matrix.a[1] - q * matrix.a[3];
c.shrink(), d.shrink();
matrix.a[0] = std::move(matrix.a[2]), matrix.a[1] = std::move(matrix.a[3]);
matrix.a[2] = std::move(c), matrix.a[3] = std::move(d);
matrix.identity = false;
}
static void apply(const Matrix &matrix, F &a, F &b) {
if (matrix.identity) return;
F c = matrix.a[0] * a + matrix.a[1] * b;
b = matrix.a[2] * a + matrix.a[3] * b, a = std::move(c);
a.shrink(), b.shrink();
}
static Matrix half(F a, F b) {
assert(a.size() > b.size());
const int m = int(a.size()) / 2;
Matrix result;
if (int(b.size()) <= m) return result;
if (a.size() <= 64) {
while (int(b.size()) > m) {
auto [q, r] = a.divmod(b);
step(result, q), a = std::move(b), b = std::move(r);
}
return result;
}
result = half(high(a, m), high(b, m));
apply(result, a, b);
if (int(b.size()) <= m) return result;
auto [q, r] = a.divmod(b);
step(result, q), a = std::move(b), b = std::move(r);
if (int(b.size()) <= m) return result;
const int k = 2 * m + 1 - int(a.size());
return multiply(half(high(a, k), high(b, k)), result);
}
static void reduce(F &a, F &b, Matrix *matrix = nullptr) {
while (!b.empty()) {
if (a.size() > b.size()) {
auto part = half(a, b);
apply(part, a, b);
if (matrix) *matrix = multiply(part, *matrix);
if (b.empty()) break;
}
auto [q, r] = a.divmod(b);
if (matrix) step(*matrix, q);
a = std::move(b), b = std::move(r);
}
}
};
}