多项式专用取模类
struct ModInt {
static const unsigned P = 998244353, G = 3, GI = 332748118;
static inline unsigned reduce(unsigned x) { return x < P ? x : x - P; }
int x;
ModInt(unsigned x = 0): x(x) {}
friend ModInt operator-(ModInt x) { return reduce(P - x.x); }
friend ModInt operator+(ModInt x, ModInt y) { return reduce(x.x + y.x); }
friend ModInt operator-(ModInt x, ModInt y) { return reduce(P + x.x - y.x); }
friend ModInt operator*(ModInt x, ModInt y) { return 1ULL * x.x * y.x % P; }
friend ModInt &operator+=(ModInt &x, ModInt y) { return x = x + y; }
friend ModInt &operator-=(ModInt &x, ModInt y) { return x = x - y; }
friend ModInt &operator*=(ModInt &x, ModInt y) { return x = x * y; }
friend ModInt qpow(ModInt x, int y) { ModInt res = 1; for (; y; y >>= 1, x *= x) if (y & 1) res *= x; return res; }
friend ModInt operator~(ModInt x) { return qpow(x, P - 2); }
friend ModInt operator/(ModInt x, ModInt y) { return x * ~y; }
friend ModInt &operator/=(ModInt &x, ModInt y) { return x *= ~y; }
friend istream &operator>>(istream &cin, ModInt &x) { return cin >> x.x; }
friend ostream &operator<<(ostream &cout, ModInt x) { return cout << x.x; }
};
struct Poly: public vector<ModInt> {
using vector<ModInt>::vector;
friend Reimu NTT(Poly &F, int o = 0) {
int len = F.size(), sz = __lg(len);
vector<ModInt> g(len >> 1); g[0] = 1;
vector<int> rv(len >> 1);
for (int i = 1; i < len >> 1; ++i) if ((rv[i] = rv[i >> 1] >> 1 | (i & 1) << sz - 1) < i) ::swap(F[i], F[rv[i]]);
for (int i = len >> 1, j; i < len; ++i) if ((j = rv[i >> 1] >> 1 | (i & 1) << sz - 1) < i) ::swap(F[i], F[j]);
for (int k = 1; k <= sz; ++k) {
int hf = 1 << k - 1;
ModInt g_ = qpow(ModInt(o ? ModInt::GI : ModInt::G), ModInt::P - 1 >> k);
for (int i = 1; i < hf; ++i) g[i] = g[i - 1] * g_;
for (auto f = F.begin(); f != F.end(); f += 1 << k) {
auto f0 = f, f1 = f + hf, gg = g.begin();
for (int j = 0; j < hf; ++j) {
ModInt F0 = *f0, F1 = *f1 * *gg++;
*f0++ = F0 + F1; *f1++ = F0 - F1;
}
}
}
if (!o) return;
ModInt inv = ~ModInt(len);
for (ModInt &x: F) x *= inv;
}
friend Poly &operator+=(Poly &F, TY(Poly) G) {
if (F.size() < G.size()) F.resize(G.size());
for (int i = 0; i < G.size(); ++i) F[i] += G[i];
return F;
}
friend Poly &operator-=(Poly &F, TY(Poly) G) {
if (F.size() < G.size()) F.resize(G.size());
for (int i = 0; i < G.size(); ++i) F[i] -= G[i];
return F;
}
friend Poly &operator*=(Poly &F, Poly G) {
if (F.empty() || G.empty()) return F = {};
int len = F.size() + G.size() - 1, len_ = 1 << __lg(len - 1) + 1;
F.resize(len_); NTT(F); G.resize(len_); NTT(G);
for (int i = 0; i < len_; ++i) F[i] *= G[i];
NTT(F, 1); F.resize(len);
return F;
}
friend Poly operator+(Poly F, TY(Poly) G) { return F += G; }
friend Poly operator-(Poly F, TY(Poly) G) { return F -= G; }
friend Poly operator*(Poly F, TY(Poly) G) { return F *= G; }
};