已经完成的:
- FFT/NTT
- 多项式乘法
- 多项式乘法逆
- 多项式除法
- 多项式积分
- 多项式求导
- 多项式膜法
- 多项式对数函数
- 多项式指数函数
- 多项式快速幂
- 多项式平方根(二次剩余)
- 多项式三角函数
- 多项式反三角函数
仍需完成的:
- 多项式复合函数
- 多项式复合逆
- 各种多项式相互转(快速)
(为了好背而没有进行进一步的优化)
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<cassert>
#include<ctime>
typedef long long ll;
typedef std::vector<ll> vec;
const ll mod = 998244353;
const ll gen = 3, img = 86583718;
const int maxn = 1E+5 + 5;
namespace IObuf {
const int LEN = 1 << 20;
char ibuf[LEN + 5], *p1 = ibuf, *p2 = ibuf;
char obuf[LEN + 5], *p3 = obuf;
inline char get() {
#ifdef ONLINE_JUDGE
return p1 == p2 && (p2 = (p1 = ibuf) + fread(ibuf, 1, LEN, stdin), p1 == p2) ? EOF : *p1++;
#endif
return getchar();
}
inline ll getll(char c = get(), ll x = 0) {
while(c < '0' || c > '9') c = get();
while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = get();
return x;
}
inline char* flush() { fwrite(obuf, 1, p3 - obuf, stdout); return p3 = obuf; }
inline void put(char c) {
#ifdef INLINE_JUDGE
p3 == obuf + LEN && flush(); *p3++ = c; return;
#endif
putchar(c);
}
char s[20]; int t = 0;
inline void putll(ll x, char suf = ' ') {
if(!x) put('0');
else {
while(x) s[++t] = x % 10 + 48, x /= 10;
while(t) put(s[t--]);
} put(suf);
}
}
using IObuf::getll;
using IObuf::putll;
inline ll fsp(ll a, ll b, const ll &mod = mod, ll res = 1) {
for(a %= mod, b %= mod - 1; b; a = a * a % mod, b >>= 1)
(b & 1) && (res = res * a % mod); return res;
}
namespace Cipolla {
ll imgn;
struct complex {
ll re, im;
inline complex(ll _r = 0, ll _i = 0) : re(_r), im(_i) {}
inline complex operator*(const complex &d) const {
return complex((re * d.re + im * d.im % mod * imgn) % mod, (re * d.im + im * d.re) % mod);
}
};
inline complex mod_fsp(complex a, ll b, complex res = 1) {
for(b %= mod - 1; b; a = a * a, b >>= 1)
if(b & 1) res = res * a; return res;
}
inline ll solve(ll x) {
if(mod == 2 || !x) return x;
ll a = 0;
while(true) {
a = 1ll * rand() * rand() % mod;
if(fsp(mod + a * a - x, mod - 1 >> 1) == mod - 1) break;
} imgn = (a * a - x + mod) % mod;
ll ans = mod_fsp(complex(a, 1), mod + 1 >> 1).re;
return std::min(ans, mod - ans);
}
}
namespace Poly {
struct poly {
vec f;
// init
inline poly(ll v = 0) : f(1) { f[0] = v; }
inline poly(const vec &_f) : f(_f) {}
inline poly(std::initializer_list<ll> init) : f(init) {}
// tools
inline ll operator[](int p) const { return p < f.size() ? f[p] : 0; }
inline ll &operator[](int p) { if(p >= f.size()) f.resize(p + 1); return f[p]; }
inline int deg() const { return f.size() - 1; }
inline void redeg(int d) { f.resize(d + 1); }
inline poly slice(int d) const {
if(d < f.size())
return vec(f.begin(), f.begin() + d + 1);
vec res(f);
return res.resize(d + 1), res;
}
inline void print(int d) const {
for(int i = 0; i <= d; ++i)
putll(operator[](i));
IObuf::put('
');
}
inline ll calc(ll x) const {
ll res = 0, tmp = 1;
for(int i = 0; i <= deg(); ++i) {
(res += f[i] * tmp) %= mod;
(tmp *= x) %= mod;
} return res;
}
// operators
inline poly operator+(const poly &P) const {
vec res(std::max(f.size(), P.f.size()));
for(int i = 0; i < res.size(); ++i)
(res[i] = operator[](i) + P[i]) >= mod && (res[i] -= mod);
return res;
}
inline poly operator-() const {
poly res(f);
for(int i = 0; i < f.size(); ++i)
res[i] && (res[i] = mod - res[i]);
return res;
}
inline poly operator-(const poly &P) const { return operator+(-P); }
inline poly operator<<(int d) const {
poly res;
for(int i = 0; i <= deg(); ++i)
res[i + d] = operator[](i);
return res;
}
inline poly operator>>(int d) const {
if(d > deg()) return poly(0);
return vec(f.begin() + d, f.end());
}
inline poly operator*(const ll v) const {
poly res(f);
for(int i = 0; i <= deg(); ++i)
(res[i] *= v) %= mod;
return res;
}
inline poly operator*(const poly &P) const; // redeg to max(deg(), P.deg())
inline poly operator/(const poly &P) const;
inline poly operator%(const poly &P) const;
inline poly mul(const poly &P) const; // redeg to deg() + P.deg()
inline poly intg(ll C) const;
inline poly der() const;
inline poly inv() const;
inline poly ln() const;
inline poly exp() const;
inline poly sqrt() const;
inline poly sin() const;
inline poly cos() const;
inline poly tan() const;
inline poly asin() const;
inline poly acos() const;
inline poly atan() const;
};
int Len = -1, rev[maxn * 4], rt[maxn * 4];
inline void NTTpre(int bit) {
Len = bit;
int N = 1 << Len;
ll stp = fsp(gen, (mod - 1) >> Len);
rt[0] = 1;
for(int i = 1; i < N; ++i)
rt[i] = rt[i - 1] * stp % mod;
}
unsigned long long tmp[maxn << 2];
inline void NTT(poly &f, int bit, int op) {
if(Len < bit) NTTpre(bit);
int N = 1 << bit;
for(int i = 0; i < N; ++i) {
rev[i] = (rev[i >> 1] >> 1 | (i & 1) << bit - 1);
tmp[i] = f[rev[i]] + (f[rev[i]] >> 31 & mod); // magic
}
for(int len = 1, p = Len - 1; len < N; len <<= 1, --p) {
for(int i = 0; i < N; i += len << 1) {
for(int k = i; k < i + len; ++k) {
ll g = tmp[k], h = tmp[k + len] * rt[k - i << p] % mod;
tmp[k] = g + h, tmp[k + len] = mod + g - h;
}
}
}
for(int i = 0; i < N; ++i) f[i] = tmp[i] % mod;
if(op == -1) {
reverse(f.f.begin() + 1, f.f.begin() + N);
ll invN = fsp(N, mod - 2);
for(int i = 0; i < N; ++i)
f[i] = f[i] * invN % mod;
}
}
bool WayToDeg = 0;
inline poly poly::operator*(const poly &P) const {
poly F(f), G = P;
int bit = ceil(log2(F.deg() + G.deg() + 1));
int N = 1 << bit;
NTT(F, bit, 1), NTT(G, bit, 1);
for(int i = 0; i < N; ++i)
(F[i] *= G[i]) %= mod;
NTT(F, bit, -1);
if(!WayToDeg) return F.slice(std::max(deg(), P.deg()));
else return F.slice(deg() + P.deg());
}
inline poly poly::operator/(const poly &P) const {
if(deg() < P.deg()) return 0;
poly g = vec(f.rbegin(), f.rend());
poly h = vec(P.f.rbegin(), P.f.rend());
g.redeg(deg() - P.deg()), h.redeg(deg() - P.deg());
poly res = g * h.inv();
res.redeg(deg() - P.deg());
reverse(res.f.begin(), res.f.end());
return res;
}
inline poly poly::operator%(const poly &P) const {
return operator-(operator/(P) * P).slice(P.deg() - 1);
}
inline poly poly::mul(const poly &P) const {
WayToDeg = 1; poly H = operator*(P);
return WayToDeg = 0, H;
}
inline poly poly::inv() const {
poly g = fsp(f[0], mod - 2), h;
for(int stp = 1; (1 << stp - 1) <= deg(); ++stp) {
int N = 1 << (stp + 1); h = slice((N >> 1) - 1);
NTT(g, stp + 1, 1), NTT(h, stp + 1, 1);
for(int i = 0; i < N; ++i)
g[i] = g[i] * (mod + 2 - h[i] * g[i] % mod) % mod;
NTT(g, stp + 1, -1), g.redeg((N >> 1) - 1);
} return g.slice(deg());
}
inline poly poly::der() const {
poly res;
for(int i = 1; i <= deg(); ++i)
res[i - 1] = f[i] * i % mod;
return res;
}
inline poly poly::intg(ll C = 0) const {
poly res = C;
for(int i = 0; i <= deg(); ++i)
res[i + 1] = f[i] * fsp(i + 1, mod - 2) % mod;
return res;
}
inline poly poly::ln() const { return (inv() * der()).intg().slice(deg()); }
inline poly poly::exp() const {
poly g = 1, h, j;
for(int stp = 1; (1 << stp - 1) <= deg(); ++stp) {
int N = 1 << stp + 1;
h = slice((N >> 1) - 1), j = g.slice((N >> 1) - 1).ln();
NTT(g, stp + 1, 1), NTT(h, stp + 1, 1), NTT(j, stp + 1, 1);
for(int i = 0; i < N; ++i)
g[i] = g[i] * (mod + 1 - j[i] + h[i]) % mod;
NTT(g, stp + 1, -1), g.redeg((N >> 1) - 1);
} return g.slice(deg());
}
inline poly poly::sqrt() const {
poly g = Cipolla::solve(operator[](0)), h, j;
for(int stp = 1; (1 << stp - 1) <= deg(); ++stp) {
int N = 1 << stp + 1;
h = slice((N >> 1) - 1), j = (g + g).slice((N >> 1) - 1).inv();
NTT(g, stp + 1, 1), NTT(h, stp + 1, 1), NTT(j, stp + 1, 1);
for(int i = 0; i < N; ++i)
g[i] = (g[i] * g[i] % mod + h[i]) * j[i] % mod;
NTT(g, stp + 1, -1), g.redeg((N >> 1) - 1);
} return g.slice(deg());
}
inline poly poly::sin() const { return (operator*(img).exp() - operator*(mod - img).exp()) * fsp(2 * img, mod - 2); }
inline poly poly::cos() const { return (operator*(img).exp() + operator*(mod - img).exp()) * fsp(2, mod - 2); }
inline poly poly::tan() const { return sin() * cos().inv(); }
inline poly poly::asin() const { return (der() * (poly(1) - operator*(*this)).sqrt().inv()).intg(); }
inline poly poly::acos() const { return (der() * -(poly(1) - operator*(*this)).sqrt().inv()).intg(); }
inline poly poly::atan() const { return (der() * (poly(1) + operator*(*this)).inv()).intg(); }
}
using Poly::poly;
int main() {
}