typedef vector<int> poly; constint mod = 998244353; constint N = 4000000 + 5;
int rf[26][N]; intfpow(int a, int b){ int res = 1; for (; b; b >>= 1, a = a * 1ll * a % mod) if (b & 1) res = res * 1ll * a % mod; return res; } voidinit(int n){ assert(n < N); int lg = __lg(n); static vector<bool> bt(26, 0); if (bt[lg] == 1) return; bt[lg] = 1; for (int i = 0; i < n; i++) rf[lg][i] = (rf[lg][i >> 1] >> 1) + ((i & 1) ? (n >> 1) : 0); } voidntt(poly &x, int lim, int op){ int lg = __lg(lim), gn, g, tmp; for (int i = 0; i < lim; i++) if (i < rf[lg][i]) swap(x[i], x[rf[lg][i]]); for (int len = 2; len <= lim; len <<= 1) { int k = (len >> 1); gn = fpow(3, (mod - 1) / len); for (int i = 0; i < lim; i += len) { g = 1; for (int j = 0; j < k; j++, g = gn * 1ll * g % mod) { tmp = x[i + j + k] * 1ll * g % mod; x[i + j + k] = (x[i + j] - tmp + mod) % mod; x[i + j] = (x[i + j] + tmp) % mod; } } } if (op == -1) { reverse(x.begin() + 1, x.begin() + lim); int inv = fpow(lim, mod - 2); for (int i = 0; i < lim; i++) x[i] = x[i] * 1ll * inv % mod; } } poly multiply(const poly &a, const poly &b){ assert(!a.empty() && !b.empty()); int lim = 1; while (lim + 1 < int(a.size() + b.size())) lim <<= 1; init(lim); poly pa = a, pb = b; pa.resize(lim); pb.resize(lim); ntt(pa, lim, 1); ntt(pb, lim, 1); for (int i = 0; i < lim; i++) pa[i] = pa[i] * 1ll * pb[i] % mod; ntt(pa, lim, -1); pa.resize(int(a.size() + b.size()) - 1); return pa; } poly prod_poly(const vector<poly>& vec){ int n = vec.size(); auto calc = [&](constauto &self, int l, int r) -> poly { if (l == r) return vec[l]; int mid = (l + r) >> 1; returnmultiply(self(self, l, mid), self(self, mid + 1, r)); }; returncalc(calc, 0, n - 1); }
// Semi-Online-Convolution poly semi_online_convolution(const poly& g, int n, int op = 0){ assert(n == g.size()); poly f(n, 0); f[0] = 1; auto CDQ = [&](constauto &self, int l, int r) -> void { if (l == r) { // exp if (op == 1 && l > 0) f[l] = f[l] * 1ll * fpow(l, mod - 2) % mod; return; } int mid = (l + r) >> 1; self(self, l, mid); poly a, b; for (int i = l; i <= mid; i++) a.push_back(f[i]); for (int i = 0; i <= r - l - 1; i++) b.push_back(g[i + 1]); a = multiply(a, b); for (int i = mid + 1; i <= r; i++) f[i] = (f[i] + a[i - l - 1]) % mod; self(self, mid + 1, r); }; CDQ(CDQ, 0, n - 1); return f; }
poly getinv(const poly &a){ assert(!a.empty()); poly res = {fpow(a[0], mod - 2)}, na = {a[0]}; int lim = 1; while (lim < int(a.size())) lim <<= 1; for (int len = 2; len <= lim; len <<= 1) { while (na.size() < len) { int tmp = na.size(); if (tmp < a.size()) na.push_back(a[tmp]); else na.push_back(0); } auto tmp = multiply(na, res); for (auto &x : tmp) x = (x > 0 ? mod - x : x); tmp[0] = ((tmp[0] + 2) >= mod) && (tmp[0] -= mod); tmp = multiply(res, tmp); tmp.resize(len); res = tmp; } res.resize(a.size()); return res; } poly exp(const poly &g){ int n = g.size(); poly b(n, 0); for (int i = 1; i < n; i++) b[i] = i * 1ll * g[i] % mod; returnsemi_online_convolution(b, n, 1); } poly ln(const poly &A){ int n = A.size(); auto C = getinv(A); poly A1(n, 0); for (int i = 0; i < n - 1; i++) A1[i] = (i + 1) * 1ll * A[i + 1] % mod; C = multiply(C, A1); for (int i = n - 1; i > 0; i--) C[i] = C[i - 1] * 1ll * fpow(i, mod - 2) % mod; C[0] = 0; C.resize(n); return C; } poly quick_pow(poly &a, int k, int k_mod_phi, bool is_k_bigger_than_mod){ assert(!a.empty()); int n = a.size(), t = -1, b; for (int i = 0; i < n; i++) if (a[i]) { t = i, b = a[i]; break; } if (t == -1 || t && is_k_bigger_than_mod || k * 1ll * t >= n) returnpoly(n, 0); poly f; for (int i = 0; i < n; i++) { if (i + t < n) f.push_back(a[i + t] * 1ll * fpow(b, mod - 2) % mod); else f.push_back(0); } f = ln(f); for (auto &x : f) x = x * 1ll * k % mod; f = exp(f); poly res; for (int i = 0; i < k * t; i++) res.push_back(0); int fb = fpow(b, k_mod_phi); for (int i = k * t; i < n; i++) res.push_back(f[i - k * t] * 1ll * fb % mod); return res; }