注意事项
- 在使用一个函数前,请确保 [lim, lim*2) 内的所有项都为 0。
- 请保证数组空间为 2 倍。
- 请确保在之前已经使用 init,参数是 lim*2。
const int MOD = 998244353, MAXN = 131072 + 5;
ll q_pow(ll a, ll b, ll p = MOD) {
ll ret = 1;
for (; b; a = a * a % p, b >>= 1) if (b & 1) ret = ret * a % p;
return ret;
}
ll inv(ll x, ll p = MOD) { return q_pow(x, p - 2, p); }
inline int add(int x, int y) { return x + y < MOD ? x + y : x + y - MOD; }
inline int dec(int x, int y) { return x < y ? x - y + MOD : x - y; }
inline void ADD(int& x, int y) { x += y; if (x >= MOD) x -= MOD; }
inline void DEC(int& x, int y) { x -= y; if (x < 0) x += MOD; }
namespace poly {
const int M = MAXN * 2;
int rev[M], fac[M], ifac[M], I[M], Wn[M*2], iWn[M*2];
void init(int LIM) {
fac[0] = fac[1] = ifac[0] = ifac[1] = I[1] = 1;
for (int i = 2; i <= LIM; ++i) {
fac[i] = 1ll * fac[i - 1] * i %MOD;
I[i] = 1ll * (MOD - MOD / i) * I[MOD % i] % MOD;
ifac[i] = 1ll * ifac[i - 1] * I[i] % MOD;
}
for (int l = 2; l <= LIM; l <<= 1) {
int g = q_pow(3, (MOD - 1) / l), ig = inv(g), wn = 1, iwn = 1;
for (int j = 0; j < (l >> 1); ++j, wn=1ll*wn*g%MOD, iwn=1ll*iwn*ig%MOD) {
Wn[l | j] = wn, iWn[l | j] = iwn;
}
}
}
void Copy(int* A, const int* B, int LIM) {
for (int i = 0; i < LIM; ++i) A[i] = B[i];
}
void NTT(int* F, int LIM, int op) {
for (int i = 0; i < LIM; ++i) if (i < rev[i]) swap(F[i], F[rev[i]]);
for (int l = 2; l <= LIM; l <<= 1) for (int i = 0; i < LIM; i += l) {
for (int j = 0; j < (l >> 1); ++j) {
int x = F[i | j];
int y = 1ll * F[i | j | (l >> 1)] * (op == -1 ? iWn[l | j] : Wn[l | j]) % MOD;
F[i | j] = add(x, y), F[i | j | (l >> 1)] = dec(x, y);
}
}
if (op == -1) {
int invLIM = inv(LIM);
for (int i = 0; i < LIM; ++i) F[i] = 1ll * F[i] * invLIM % MOD;
}
}
int Tmul[M];
void Multiply(int* A, const int* B, int LIM, int L) {
for (int i = 0; i < LIM; ++i) Tmul[i] = B[i];
for (int i = LIM; i < (LIM << 1); ++i) Tmul[i] = 0;
for (int i = 0; i < (LIM << 1); ++i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << L);
NTT(A, LIM << 1, 1), NTT(Tmul, LIM << 1, 1);
for (int i = 0; i < (LIM << 1); ++i) A[i] = 1ll * A[i] * Tmul[i] % MOD;
NTT(A, LIM << 1, -1);
for (int i = LIM; i < (LIM << 1); ++i) A[i] = 0;
}
int Tinv[M];
void Inv(int* G, const int* F, int LIM, int L) {
for (int i = 0; i < LIM; ++i) G[i] = 0;
G[0] = inv(F[0]);
for (int l = 1, lim = 2; l <= L; ++l, lim <<= 1) {
for (int i = 0; i < lim; ++i) Tinv[i] = F[i];
for (int i = lim; i < (lim << 1); ++i) Tinv[i] = 0;
for (int i = 0; i < (lim << 1); ++i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << l);
NTT(G, lim << 1, 1), NTT(Tinv, lim << 1, 1);
for (int i = 0; i < (lim << 1); ++i) {
G[i] = dec(add(G[i], G[i]), 1ll * Tinv[i] * G[i] % MOD * G[i] % MOD);
}
NTT(G, lim << 1, -1);
for (int i = lim; i < (lim << 1); ++i) G[i] = 0;
}
for (int i = 0; i < (LIM << 1); ++i) Tinv[i] = 0;
}
void DAO(int* F, int LIM) {
for (int i = 0; i < LIM - 1; ++i) F[i] = 1ll * F[i + 1] * (i + 1) % MOD;
F[LIM - 1] = 0;
}
void JI(int* F, int LIM) {
for (int i = LIM - 1; i >= 1; --i) F[i] = 1ll * F[i - 1] * I[i] % MOD;
F[0] = 0;
}
int Tln[M];
void Ln(int* G, const int* F, int LIM, int L) {
Copy(G, F, LIM), Inv(Tln, G, LIM, L), DAO(G, LIM);
Multiply(G, Tln, LIM, L);
JI(G, LIM);
for (int i = 0; i < (LIM << 1); ++i) Tln[i] = 0;
for (int i = LIM; i < (LIM << 1); ++i) G[i] = 0;
}
int Texp[M];
void Exp(int* G, const int* F, int LIM, int L) {
for (int i = 0; i < (LIM << 1); ++i) G[i] = 0;
G[0] = 1;
for (int l = 1, lim = 2; l <= L; ++l, lim <<= 1) {
Ln(Texp, G, lim, l);
for (int i = 0; i < lim; ++i) Texp[i] = dec(F[i], Texp[i]);
ADD(Texp[0], 1);
Multiply(G, Texp, lim, l);
for (int i = lim; i < (lim << 1); ++i) G[i] = 0;
}
for (int i = LIM; i < (LIM << 1); ++i) G[i] = 0;
for (int i = 0; i < (LIM << 1); ++i) Texp[i] = 0;
}
};
using namespace poly;