#include #include #include #include using namespace std; using ll=long long; //geneerated by Codex outside of contest time. #include #include #include #include #include #include template class FPS { public: using value_type = Field; using size_type = std::size_t; explicit FPS(size_type n = 0) : coefficients_(n) {} // 長さ n の零形式的冪級数を作る。 template explicit FPS(size_type n = 0, U v = 0) : coefficients_(n,v) {} // Field に変換できる型 U の係数列から作る。 template explicit FPS(const std::vector& coefficients) { coefficients_.reserve(coefficients.size()); for (const auto& coefficient : coefficients) { coefficients_.emplace_back(coefficient); } } size_type size() const noexcept { return coefficients_.size(); } void resize(size_type n) { coefficients_.resize(n); } Field& operator[](size_type i) { return coefficients_[i]; } const Field& operator[](size_type i) const { return coefficients_[i]; } FPS& operator+=(const FPS& rhs) { const size_type old_size = size(); resize(std::max(size(), rhs.size())); for (size_type i = 0; i < rhs.size(); ++i) { if (i < old_size) { coefficients_[i] += rhs[i]; } else { coefficients_[i] = rhs[i]; } } return *this; } FPS& operator-=(const FPS& rhs) { const size_type old_size = size(); resize(std::max(size(), rhs.size())); for (size_type i = 0; i < rhs.size(); ++i) { if (i < old_size) { coefficients_[i] -= rhs[i]; } else { coefficients_[i] = -rhs[i]; } } return *this; } FPS& operator*=(const FPS& rhs) { return *this = *this * rhs; } // 形式的微分を求める。 FPS derivative() const { if (size() == 0) { return FPS(); } FPS result(size() - 1); for (size_type i = 1; i < size(); ++i) { result[i - 1] = coefficients_[i] * Field(i); } return result; } // 競技プログラミングで一般的な名前も用意する。 FPS diff() const { return derivative(); } // 定数項を 0 として形式的積分を求める。 FPS integral() const { FPS result(size() + 1); for (size_type i = 0; i < size(); ++i) { result[i + 1] = coefficients_[i] / Field(i + 1); } return result; } // this * result == 1 (mod x^size()) となる乗法逆元を求める。 FPS inv() const { assert(size() > 0 && coefficients_[0] != Field(0)); FPS result(std::vector{coefficients_[0].inv()}); while (result.size() < size()) { const size_type next_size = std::min(size(), result.size() * 2); FPS prefix(next_size); for (size_type i = 0; i < next_size; ++i) { prefix[i] = coefficients_[i]; } FPS correction = prefix * result; correction.resize(next_size); for (auto& coefficient : correction.coefficients_) { coefficient = -coefficient; } correction[0] += Field(2); result *= correction; result.resize(next_size); } return result; } // log(this) (mod x^size()) を O(N log N) で求める。 FPS log() const { assert(size() > 0 && coefficients_[0] == Field(1)); FPS result = derivative() * inv(); result.resize(size() - 1); result = result.integral(); result.resize(size()); return result; } // exp(this) (mod x^size()) を Newton 法により O(N log N) で求める。 FPS exp() const { if (size() == 0) { return FPS(); } assert(coefficients_[0] == Field(0)); FPS result(std::vector{Field(1)}); while (result.size() < size()) { const size_type next_size = std::min(size(), result.size() * 2); FPS prefix(next_size); for (size_type i = 0; i < next_size; ++i) { prefix[i] = coefficients_[i]; } result.resize(next_size); FPS correction = prefix - result.log(); correction[0] += Field(1); result *= correction; result.resize(next_size); } return result; } FPS& operator/=(const FPS& rhs) { return *this = *this / rhs; } friend FPS operator+(FPS lhs, const FPS& rhs) { lhs += rhs; return lhs; } friend FPS operator-(FPS lhs, const FPS& rhs) { lhs -= rhs; return lhs; } // atcoder::convolution への依存は積の定義だけに閉じ込める。 friend FPS operator*(const FPS& lhs, const FPS& rhs) { return FPS(atcoder::convolution(lhs.coefficients_, rhs.coefficients_)); } friend FPS operator/(const FPS& lhs, FPS rhs) { if (lhs.size() == 0) { return FPS(); } rhs.resize(std::max(rhs.size(), lhs.size())); FPS quotient = lhs * rhs.inv(); quotient.resize(std::max(lhs.size(), rhs.size())); return quotient; } private: std::vector coefficients_; }; // generated by Codex outside of contest time. #include #include #include #include #include //#include "fps.cpp" // i = 0, 1, ..., m - 1 に対する [x^k] g(x)f(x)^i を列挙する。 // // P(x,y) / Q(x,y) = g(x) / (1-yf(x)) に Graeffe 変換を適用する。 // 二変数多項式は配列にはせず、x のブロック幅 stride を用いて // [x^a y^b] を 1 本の FPS の [x^(a+stride*b)] に格納する。 template std::vector power_projection(const FPS& f, const FPS& g, std::size_t m, std::size_t k) { if (m == 0) { return {}; } // 目標次数を n-1 (n は 2 冪) にそろえる。 std::size_t n = 1; while (n <= k) { assert(n <= std::numeric_limits::max() / 2); n *= 2; } const std::size_t shift = n - 1 - k; // 各 y ブロックの間隔を 2n にすると、積における x 次数の和が // 隣の y ブロックへ桁上がりしない。 std::size_t stride = 2 * n; std::size_t p_y_size = 1; std::size_t q_y_size = std::min(m, 2); FPS p(n); for (std::size_t i = 0; i < g.size() && i + shift < n; ++i) { p[i + shift] = g[i]; } FPS q(q_y_size == 1 ? n : stride + n); q[0] = Field(1); if (q_y_size == 2) { for (std::size_t i = 0; i < f.size() && i < n; ++i) { q[stride + i] = -f[i]; } } while (n > 1) { // q(-x,y)。符号はブロック内の x 次数だけで決まる。 FPS q_minus = q; for (std::size_t y = 0; y < q_y_size; ++y) { const std::size_t base = y * stride; for (std::size_t x = 1; x < n; x += 2) { q_minus[base + x] = -q_minus[base + x]; } } FPS pq = p * q_minus; FPS qq = q * q_minus; const std::size_t next_n = n / 2; const std::size_t next_stride = n; const std::size_t next_p_y_size = std::min(m, p_y_size + q_y_size - 1); const std::size_t next_q_y_size = std::min(m, q_y_size + q_y_size - 1); FPS next_p((next_p_y_size - 1) * next_stride + next_n); FPS next_q((next_q_y_size - 1) * next_stride + next_n); // P(x,y)Q(-x,y) の x 奇数次部分と、 // Q(x,y)Q(-x,y) の x 偶数次部分を抜き出す。 for (std::size_t y = 0; y < next_p_y_size; ++y) { const std::size_t old_base = y * stride; const std::size_t new_base = y * next_stride; for (std::size_t x = 1; x < n; x += 2) { if (old_base + x < pq.size()) { next_p[new_base + x / 2] = pq[old_base + x]; } } } for (std::size_t y = 0; y < next_q_y_size; ++y) { const std::size_t old_base = y * stride; const std::size_t new_base = y * next_stride; for (std::size_t x = 0; x < n; x += 2) { if (old_base + x < qq.size()) { next_q[new_base + x / 2] = qq[old_base + x]; } } } p = next_p; q = next_q; p_y_size = next_p_y_size; q_y_size = next_q_y_size; n = next_n; stride = next_stride; } // x の幅が 1 になったので、ブロック先頭を集めて通常の FPS に戻す。 FPS numerator(m); FPS denominator(m); for (std::size_t y = 0; y < p_y_size; ++y) { numerator[y] = p[y * stride]; } for (std::size_t y = 0; y < q_y_size; ++y) { denominator[y] = q[y * stride]; } assert(denominator[0] != Field(0)); FPS answer = numerator * denominator.inv(); answer.resize(m); std::vector result(m); for (std::size_t i = 0; i < m; ++i) { result[i] = answer[i]; } return result; } // generated by Codex outside of contest time. #include #include #include #include //#include "fps.cpp" namespace multipoint_evaluation_internal { template FPS trim(FPS f) { while (f.size() > 0 && f[f.size() - 1] == Field(0)) { f.resize(f.size() - 1); } return f; } // a mod b を、反転した FPS の除算を用いて求める。 // このファイル内で用いる b はすべてモニックである。 template FPS polynomial_remainder(FPS a, const FPS& b) { a = trim(a); if (a.size() < b.size()) { return a; } const std::size_t quotient_size = a.size() - b.size() + 1; FPS reversed_a(quotient_size); FPS reversed_b(quotient_size); for (std::size_t i = 0; i < quotient_size; ++i) { reversed_a[i] = a[a.size() - 1 - i]; if (i < b.size()) { reversed_b[i] = b[b.size() - 1 - i]; } } FPS reversed_quotient = reversed_a * reversed_b.inv(); reversed_quotient.resize(quotient_size); FPS quotient(quotient_size); for (std::size_t i = 0; i < quotient_size; ++i) { quotient[i] = reversed_quotient[quotient_size - 1 - i]; } FPS remainder = a - quotient * b; if (remainder.size() >= b.size()) { remainder.resize(b.size() - 1); } return trim(remainder); } } // namespace multipoint_evaluation_internal // 任意の相異なるとは限らない点 x[i] で f を評価する。 // 積木と剰余計算により O(M(N) log N) = O(N log^2 N)。 template std::vector multipoint_evaluation(const FPS& f, const std::vector& x) { const std::size_t point_count = x.size(); if (point_count == 0) { return {}; } std::size_t leaf_count = 1; while (leaf_count < point_count) { leaf_count *= 2; } std::vector> product_tree(2 * leaf_count); for (std::size_t i = 0; i < leaf_count; ++i) { if (i < point_count) { product_tree[leaf_count + i] = FPS(std::vector{-Field(x[i]), Field(1)}); } else { product_tree[leaf_count + i] = FPS(std::vector{Field(1)}); } } for (std::size_t i = leaf_count; i-- > 1;) { product_tree[i] = product_tree[2 * i] * product_tree[2 * i + 1]; } std::vector> remainder_tree(2 * leaf_count); remainder_tree[1] = multipoint_evaluation_internal::polynomial_remainder(f, product_tree[1]); for (std::size_t i = 1; i < leaf_count; ++i) { remainder_tree[2 * i] = multipoint_evaluation_internal::polynomial_remainder( remainder_tree[i], product_tree[2 * i]); remainder_tree[2 * i + 1] = multipoint_evaluation_internal::polynomial_remainder( remainder_tree[i], product_tree[2 * i + 1]); } std::vector result(point_count, Field(0)); for (std::size_t i = 0; i < point_count; ++i) { const FPS& value = remainder_tree[leaf_count + i]; if (value.size() > 0) { result[i] = value[0]; } } return result; } // x[i] = a r^i であることを仮定して f(x[i]) を列挙する。 // r^(ij) = r^C(i+j,2) / (r^C(i,2) r^C(j,2)) を用いる chirp 変換で、 // 1 回の FPS の積に帰着する。計算量は O(M(N)) = O(N log N)。 template std::vector multipoint_evaluation_geometric( const FPS& f, const std::vector& x) { const std::size_t point_count = x.size(); std::vector result(point_count, Field(0)); if (point_count == 0) { return result; } const Field constant = f.size() == 0 ? Field(0) : f[0]; const Field first = Field(x[0]); if (first == Field(0)) { // 等比数列で初項が 0 なら全評価点が 0。 std::fill(result.begin(), result.end(), constant); return result; } if (point_count == 1) { Field power = Field(1); for (std::size_t j = 0; j < f.size(); ++j) { result[0] += f[j] * power; power *= first; } return result; } const Field ratio = Field(x[1]) / first; if (ratio == Field(0)) { // a, 0, 0, ... の場合。逆元を使う chirp 変換から外して扱う。 Field power = Field(1); for (std::size_t j = 0; j < f.size(); ++j) { result[0] += f[j] * power; power *= first; } std::fill(result.begin() + 1, result.end(), constant); return result; } const std::size_t degree_size = f.size(); if (degree_size == 0) { return result; } // chirp[t] = r^{t(t-1)/2} を乗算だけで列挙する。 const std::size_t chirp_size = point_count + degree_size - 1; std::vector chirp(chirp_size, Field(1)); Field ratio_power = Field(1); for (std::size_t t = 1; t < chirp_size; ++t) { chirp[t] = chirp[t - 1] * ratio_power; ratio_power *= ratio; } FPS left(degree_size); Field first_power = Field(1); Field inverse_chirp = Field(1); const Field inverse_ratio = ratio.inv(); Field inverse_ratio_power = Field(1); for (std::size_t j = 0; j < degree_size; ++j) { left[degree_size - 1 - j] = f[j] * first_power * inverse_chirp; first_power *= first; inverse_chirp *= inverse_ratio_power; inverse_ratio_power *= inverse_ratio; } FPS product = left * FPS(chirp); inverse_chirp = Field(1); inverse_ratio_power = Field(1); for (std::size_t i = 0; i < point_count; ++i) { result[i] = product[degree_size - 1 + i] * inverse_chirp; inverse_chirp *= inverse_ratio_power; inverse_ratio_power *= inverse_ratio; } return result; } int main(){ cin.tie(nullptr); ios::sync_with_stdio(false); int n,m; cin>>n>>m; FPS f(n),g(n),h(n); for(int i=0;i>t; f[i]=t; } for(int i=0;i>t; g[i]=t; } for(int i=0;i>t; h[i]=t; } auto v=power_projection(g,FPS(1,1),n,n-1); FPS r(n); using mint=atcoder::modint998244353; for(int i=0;i x(m,1); for(int i=1;i