タイトルのままです 再帰アレルギーの方のために
じゃなくて普通にこっち先に思い付いた
方針は公式解説と同じです 別にそんな速くはなさそう
二冪の prefix にFFT のマージテクでできたやつかけていけばいいよねってだけ
自分の NTT でやったら 661ms で ACL だと 457 ms で悲しくなりました 書き直したいです
コード
#include <bits/stdc++.h> #include <atcoder/convolution> #include <atcoder/modint> using namespace std; using namespace atcoder; using mint = modint998244353; constexpr int mod = 998244353; int p2(int n){ while(n & (n - 1)) ++n; return n; } vector<mint> pre(const vector<mint> &a, int n){ return {begin(a), begin(a) + n}; } int main() { int n, a; cin >> n >> a; if(n == 1){ cout << a % mod << "\n"; return 0; } vector<mint> c(n + 1, 1), inv(n + 1, 1); for(int i = 2; i <= n; ++i) inv[i] = -inv[mod % i] * (mod / i); for(int i = 0; i < n; ++i) c[i + 1] = c[i] * (a - i) * inv[i + 1]; c.erase(begin(c), begin(c) + 2); c.resize(p2(c.size())); stack<vector<mint>> sf, sa; sf.push(c); for(int i = 0; i < n - 1; ++i){ vector<mint> f = sf.top(); while(int(f.size()) > 1){ sf.push(f = pre(f, f.size() / 2)); } mint x = sf.top()[0]; sf.pop(); if(i == n - 2){ cout << x.val() << "\n"; return 0; } f = {1, x}; while(!sa.empty()){ vector<mint> g = sa.top(); if(f.size() != g.size()) break; sa.pop(); f = convolution(f, g); } sa.push(f); int m = int(sf.top().size()); f = convolution(f, sf.top()); sf.pop(); f = pre(f, m); sf.emplace(begin(f) + (m/2), end(f)); } }
提出結果