多项式全家桶

多项式乘法

FFT

#include<cstdio> #include<cmath> #include<iostream> using namespace std; const double PI = acos(-1.0); const int MAXN = 1e7 + 5; inline int read() { char ch; while((ch = getchar()) && (ch < '0' || ch > '9')); return ch - '0'; } int n, m, len, lim = 1; int r[MAXN]; struct complex { double x, y; complex (double xx = 0, double yy = 0) {x = xx, y = yy;} complex operator + (complex b) {return complex(x + b.x, y + b.y);}; complex operator - (complex b) {return complex(x - b.x, y - b.y);}; complex operator * (complex b) {return complex(x * b.x - y * b.y, x * b.y + y * b.x);}; }f[MAXN], g[MAXN]; void FFT(complex *A, int op) { for(int i = 0; i < lim; i++) if(i < r[i]) swap(A[i], A[r[i]]); for(int mid = 1; mid < lim; mid <<= 1) { complex wn(cos(PI / mid), op * sin(PI / mid)); for(int r = mid << 1, j = 0; j < lim; j += r) { complex w(1, 0); for(int k = 0; k < mid; k++, w = w * wn) { complex x = A[j + k], y = w * A[j + k + mid]; A[j + k] = x + y, A[j + k + mid] = x - y; } } } } int main() { scanf("%d%d",&n, &m); for(int i = 0; i <= n; i++) f[i].x = read(); for(int i = 0; i <= m; i++) g[i].x = read(); len = n + m; int l = 0; while(lim <= n + m) lim <<= 1, l++; for(int i = 0; i < lim; i++) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1)); FFT(f, 1); FFT(g, 1); for(int i = 0; i <= lim; i++) f[i] = f[i] * g[i]; FFT(f, -1); for(int i = 0; i <= n + m; i++) printf("%d ",(int)(f[i].x / lim + 0.5)); return 0; }

NTT

#include<cstdio> #include<iostream> #define LL long long using namespace std; inline int read() { int res = 0, f = 1; char ch; while((ch = getchar()) && (ch < '0' || ch > '9') && ch != '0'); (ch == '0') ? f = -1 : res = ch - '0'; while((ch = getchar()) && ch >= '0' && ch <= '9') res = (res << 3) + (res << 1) + ch - '0'; return res * f; } int prnum[21]; inline void print(LL x) { if(x == 0) putchar('0'); else { if(x < 0) putchar('-'), x = -x; register int prcnt = 0; while(x) { prnum[++prcnt] = x % 10; x /= 10; } for(register int i = prcnt; i >= 1; i--) putchar(prnum[i] + '0'); } } const int MAXN = 1e6 + 5; const LL P = 998244353, G = 3, Gi = 332748118; int n, m; int lim = 1, len, rev[MAXN << 2]; LL f[MAXN << 2], g[MAXN << 2]; inline LL expow(LL x, LL y) { LL res = 1; while(y) { if(y & 1) res = res * x % P; x = x * x % P, y >>= 1; } return res; } inline void NTT(LL *A, int op) { for(int i = 0; i < lim; i++) if(i > rev[i]) swap(A[i], A[rev[i]]); for(int mid = 1; mid < lim; mid <<= 1) { LL wn = expow((op == 1 ? G : Gi), (P - 1) / (mid << 1)); for(int r = (mid << 1), j = 0; j < lim; j += r) { LL w = 1; for(int k = 0; k < mid; k++, w = w * wn % P) { LL x = A[j + k], y = w * A[j + k + mid] % P; A[j + k] = (x + y) % P, A[j + k + mid] = ((x - y) % P + P) % P; } } } } int main() { n = read(); m = read(); for(int i = 0; i <= n; i++) f[i] = read(); for(int i = 0; i <= m; i++) g[i] = read(); while(lim <= n + m) lim <<= 1, ++len; for(int i = 0; i < lim; i++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (len - 1)); NTT(f, 1); NTT(g, 1); for(int i = 0; i < lim; i++) f[i] = f[i] * g[i] % P; NTT(f, -1); LL inv = expow(lim, P - 2); for(int i = 0; i <= n + m; i++) print(f[i] * inv % P), putchar(' '); return 0; }

多项式乘法逆

g 使得 FG1 (mod xn)
FH1 (mod xn2)

GH0 (mod xn2)

G22GH+H20 (mod xn)

两边同乘 F,得:

G2H+FH20 (mod xn)

G(2FH)H (mod xn)

当递归到常数项时,H0F0 在模 p 意义下的逆元。

#include<cstdio> #include<iostream> #define LL long long using namespace std; inline LL read() { LL res = 0, f = 1; char ch; while((ch = getchar()) && (ch < '0' || ch > '9') && ch != '-'); (ch == '-') ? f = -1 : res = ch - '0'; while((ch = getchar()) && ch >= '0' && ch <= '9') res = (res << 3) + (res << 1) + ch - '0'; return res * f; } int prnum[21]; inline void print(LL x) { if(x == 0) putchar('0'); else { register int prcnt = 0; while(x) { prnum[++prcnt] = x % 10; x /= 10; } for(register int i = prcnt; i >= 1; i--) putchar(prnum[i] + '0'); } } const int MAXN = 4e5 + 5; const LL MOD = 998244353, G = 3, Gi = 332748118; int n, rev[MAXN]; LL f[MAXN], g[MAXN], c[MAXN]; inline LL expow(LL x, LL y) { LL res = 1; while(y) { if(y & 1) res = res * x % MOD; y >>= 1, x = x * x % MOD; } return res; } inline void NTT(LL *A, int lim, int op) { for(int i = 0; i < lim; i++) if(i > rev[i]) swap(A[i], A[rev[i]]); for(int mid = 1; mid < lim; mid <<= 1) { LL wn = expow((op == 1) ? G : Gi, (MOD - 1) / (mid << 1)); for(int r = (mid << 1), j = 0; j < lim; j += r) { LL w = 1; for(int k = 0; k < mid; k++, w = wn * w % MOD) { LL x = A[j + k], y = w * A[j + k + mid] % MOD; A[j + k] = (x + y) % MOD, A[j + k + mid] = ((x - y) % MOD + MOD) % MOD; } } } } void solve(int len0, LL *a, LL *b) { b[0] = expow(a[0], MOD - 2); for(int len = 1; len < (len0 << 1); len <<= 1) { int lim = 1, tot = 0; while(lim < (len << 1)) lim <<= 1, ++tot; for(int i = 1; i < lim; i++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (tot - 1)); for(int i = 0; i < len; i++) c[i] = a[i]; for(int i = len; i < lim; i++) c[i] = 0; NTT(c, lim, 1); NTT(b, lim, 1); for(int i = 0; i < lim; i++) b[i] = ((2 - c[i] * b[i]) % MOD * b[i] % MOD + MOD) % MOD; NTT(b, lim, -1); LL inv = expow(lim, MOD - 2); for(int i = 0; i < len; i++) b[i] = b[i] * inv % MOD; for(int i = len; i < lim; i++) b[i] = 0; } } int main() { n = read(); for(int i = 0; i < n; i++) f[i] = read(); solve(n, f, g); for(int i = 0; i < n; i++) print(g[i]), putchar(' '); return 0; }

多项式开根

G 使得 G2F (mod xn)
H2F (mod xn2)

GH0 (mod xn2)

G22GH+H20 (mod xn)

F2GH+H20 (mod xn)

G12(FH1+H) (mod xn)

当递归到常数项时,令 H0F0 在模 p 意义下的二次剩余。

#include<cstdio> #include<iostream> #include<cstdlib> #include<ctime> #define LL long long using namespace std; inline int read() { int res = 0, f = 1; char ch; while((ch = getchar()) && (ch < '0' || ch > '9') && ch != '-'); (ch == '-') ? f = -1 : res = ch - '0'; while((ch = getchar()) && ch >= '0' && ch <= '9') res = (res << 3) + (res << 1) + ch - '0'; return res * f; } int prnum[21]; inline void print(LL x) { if(x == 0) putchar('0'); else { register int prcnt = 0; while(x) { prnum[++prcnt] = x % 10; x /= 10; } for(register int i = prcnt; i >= 1; i--) putchar(prnum[i] + '0'); } } const int MAXN = 4e5 + 5; const LL MOD = 998244353, G = 3, Gi = 332748118, inv2 = 499122177; int n, rev[MAXN]; LL pow_i, f[MAXN], g[MAXN], h[MAXN], q[MAXN], c[MAXN]; struct complex { LL a, b; }; inline complex operator * (const complex &x, const complex &y) { return (complex){(x.a * y.a % MOD + x.b * y.b % MOD * pow_i % MOD) % MOD, (x.a * y.b % MOD + x.b * y.a % MOD) % MOD}; } inline int randmod() { return 1ll * rand() * rand() % MOD; } inline LL expow(LL x, LL y) { LL res = 1; while(y) { if(y & 1) res = res * x % MOD; y >>= 1, x = x * x % MOD; } return res; } inline complex expow_i(complex x, int y) { complex res = {1, 0}; while(y) { if(y & 1) res = res * x; y >>= 1, x = x * x; } return res; } inline LL cipolla(LL x) { x %= MOD; if(x == 0) return 0; if(expow(x, (MOD - 1) / 2) == MOD - 1) return -1; LL a, res; while(1) { a = randmod(); pow_i = ((a * a % MOD - x) % MOD + MOD) % MOD; if(expow(pow_i, (MOD - 1) / 2) == MOD - 1) break; } res = expow_i((complex){a, 1}, (MOD + 1) / 2).a; return min(res, MOD - res); } void NTT(LL *A, int lim, int op) { for(int i = 0; i < lim; i++) if(i > rev[i]) swap(A[i], A[rev[i]]); for(int mid = 1; mid < lim; mid <<= 1) { LL wn = expow((op == 1 ? G : Gi), (MOD - 1) / (mid << 1)); for(int r = (mid << 1), j = 0; j < lim; j += r) { LL w = 1; for(int k = 0; k < mid; k++, w = w * wn % MOD) { LL x = A[j + k], y = w * A[j + k + mid]; A[j + k] = (x + y) % MOD, A[j + k + mid] = ((x - y) % MOD + MOD) % MOD; } } } } void getinv(int len, LL *a, LL *b) { if(len == 1) return b[0] = expow(a[0], MOD - 2), void(); getinv((len + 1) >> 1, a, b); int lim = 1, tot = 0; while(lim < (len << 1)) lim <<= 1, ++tot; for(int i = 1; i < lim; i++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (tot - 1)); for(int i = 0; i < len; i++) c[i] = a[i]; for(int i = len; i < lim; i++) c[i] = 0; NTT(c, lim, 1); NTT(b, lim, 1); for(int i = 0; i < lim; i++) b[i] = ((2ll - c[i] * b[i] % MOD) * b[i] % MOD + MOD) % MOD; NTT(b, lim, 0); LL inv = expow(lim, MOD - 2); for(int i = 0; i < len; i++) b[i] = b[i] * inv % MOD; for(int i = len; i < lim; i++) b[i] = 0; } void getsqrt(int len, LL *a, LL *b) { if(len == 1) return b[0] = cipolla(a[0]), void(); getsqrt((len + 1) >> 1, a, b); int lim = 1, tot = 0; while(lim < (len << 1)) lim <<= 1, ++tot; for(int i = 0; i < lim; i++) h[i] = 0; getinv(len, b, h); for(int i = 1; i < lim; i++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (tot - 1)); for(int i = 0; i < len; i++) q[i] = a[i]; for(int i = len; i < lim; i++) q[i] = 0; NTT(q, lim, 1); NTT(h, lim, 1); for(int i = 0; i < lim; i++) q[i] = q[i] * h[i] % MOD; NTT(q, lim, 0); LL inv = expow(lim, MOD - 2); for(int i = 0; i < len; i++) b[i] = (b[i] + q[i] * inv % MOD) % MOD * inv2 % MOD; for(int i = len; i < lim; i++) b[i] = 0; } int main() { srand((unsigned)time(0)); n = read(); for(int i = 0; i < n; i++) f[i] = read(); getsqrt(n, f, g); for(int i = 0; i < n; i++) print(g[i]), putchar(' '); return 0; }

多项式除法

Q,R,使得 F(x)G(x)Q(x)+R(x) (mod xn)(F(x)nG(x)m)
若消除 R(x) 的影响,则可以直接通过多项式乘法逆求出 G(x),再直接代入,即可求出 R(x)
AR(x)=xdegFA(1x)

F(1x)G(1x)Q(1x)+R(1x) (mod xn)

xnF(1x)xmG(1x)xnmQ(1x)+xnR(1x) (mod xn)

FR(x)GR(x)QR(x)+xnm+1RR(x) (mod xn)

FR(x)GR(x)QR(x) (mod xnm+1)

因为 Q(x) 仅有 nm 项,所以模 xnm+1 并不影响。

QR(x)FR(x)GR1(x)

所以先求出 GR(x) 的逆,再做多项式乘法求出 Q(x)
然后,通过

R(x)F(x)G(x)Q(x)

求出 R(x)
写的时候注意边界问题,及时清零。

#include<cstdio> #include<algorithm> #define LL long long using namespace std; inline int read() { int res = 0, f = 1; char ch; while((ch = getchar()) && (ch < '0' || ch > '9') && ch != '0'); (ch == '0') ? f = -1 : res = ch - '0'; while((ch = getchar()) && ch >= '0' && ch <= '9') res = (res << 3) + (res << 1) + ch - '0'; return res * f; } int prnum[21]; inline void print(LL x) { if(x == 0) putchar('0'); else { if(x < 0) putchar('-'), x = -x; register int prcnt = 0; while(x) { prnum[++prcnt] = x % 10; x /= 10; } for(register int i = prcnt; i >= 1; i--) putchar(prnum[i] + '0'); } } const int MAXN = 4e5 + 5; const LL MOD = 998244353, G = 3, Gi = 332748118; int n, m, rev[MAXN]; LL f[MAXN], g[MAXN], g0[MAXN], gr[MAXN], q[MAXN], c[MAXN]; inline LL expow(LL x, LL y) { LL res = 1; while(y) { if(y & 1) res = res * x % MOD; y >>= 1, x = x * x % MOD; } return res; } inline void NTT(LL *A, int lim, int op) { for(int i = 0; i < lim; i++) if(i > rev[i]) swap(A[i], A[rev[i]]); for(int mid = 1; mid < lim; mid <<= 1) { LL wn = expow((op == 1 ? G : Gi), (MOD - 1) / (mid << 1)); for(int r = (mid << 1), j = 0; j < lim; j += r) { LL w = 1; for(int k = 0; k < mid; k++, w = w * wn % MOD) { LL x = A[j + k], y = w * A[j + k + mid] % MOD; A[j + k] = (x + y) % MOD, A[j + k + mid] = ((x - y) % MOD + MOD) % MOD; } } } } void getinv(int len, LL *a, LL *b) { if(len == 1) return b[0] = expow(a[0], MOD - 2), void(); getinv((len + 1) >> 1, a, b); int lim = 1, tot = 0; while(lim < (len << 1)) lim <<= 1, ++tot; for(int i = 1; i < lim; i++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (tot - 1)); for(int i = 0; i < len; i++) c[i] = a[i]; for(int i = len; i < lim; i++) c[i] = 0; NTT(c, lim, 1); NTT(b, lim, 1); for(int i = 0; i < lim; i++) b[i] = ((2 - c[i] * b[i]) % MOD * b[i] % MOD + MOD) % MOD; NTT(b, lim, -1); LL inv = expow(lim, MOD - 2); for(int i = 0; i < len; i++) b[i] = b[i] * inv % MOD; for(int i = len; i < lim; i++) b[i] = 0; } void getmul(int lena, int lenb, int lenx, LL *a, LL *b) { int lim = 1, tot = 0; while(lim <= (lena + lenb)) lim <<= 1, ++tot; for(int i = 1; i < lim; i++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (tot - 1)); NTT(a, lim, 1); NTT(b, lim, 1); for(int i = 0; i < lim; i++) a[i] = a[i] * b[i] % MOD; NTT(a, lim, -1); LL inv = expow(lim, MOD - 2); for(int i = 0; i <= lenx; i++) a[i] = a[i] * inv % MOD; for(int i = lenx + 1; i < lim; i++) a[i] = 0; } int main() { n = read(); m = read(); for(int i = 0; i <= n; i++) f[i] = q[n - i] = read(); for(int i = 0; i <= m; i++) g[i] = read(), g0[m - i] = g[i]; for(int i = n - m + 1; i <= m; i++) g0[i] = 0; getinv(n - m + 1, g0, gr); getmul(n, n - m, n - m, q, gr); reverse(q, q + n - m + 1); for(int i = 0; i <= n - m; i++) print(q[i]), putchar(' '); putchar('\n'); getmul(m, n - m, m, g, q); for(int i = 0; i < m; i++) print(((f[i] - g[i]) % MOD + MOD) % MOD), putchar(' '); putchar('\n'); return 0; }

多项式牛顿迭代

前置芝士

1.乘法求导法则:

(f(x)g(x))=f(x)g(x)+f(x)g(x)

2.链式求导法则:

(g(f(x)))=g(f(x))f(x)

3.可以 O(n) 对一个多项式进行求导和积分。设 f(x)=iaixi,则

df(x)dx=f(x)=i(i+1)ai+1xi

f(x)dx=i>0ai1ixi

  1. f(x)a 处的泰勒展开为:

i=0difdx(a)(xa)ii!

描述

给定 g(x),已知 f(x) 满足:

g(f(x))0 (mod xn)

求在模 xn 意义下的 f(x)

牛顿迭代

倍增。
n=1 时,[x0]g(f(x)) 可求。

f(x) 在模 xn2 意义下的解为 f0(x),要求在模 xn 意义下的解 f(x)

g(f(x))f0(x) 处进行泰勒展开,得:

i=0digdx(f0(x))(f(x)f0(x))ii!

因为 f(x)f0(x) 的最低非零项次数为 n2,所以

(f(x)f0(x))i0 (mod xn)(i2)

所以

i=0digdx(f0(x))(f(x)f0(x))ii!=g(f0(x))g(f(x)f0(x))

所以

f(x)=f0(x)g(f0(x))g(f0(x))

应用

多项式求逆

给定 h(x),求 f(x)=h1(x)

g(f(x))=1f(x)h(x),所以

f(x)=f0(x)1f0(x)h(x)1f02(x)=f0(x)+f02(x)(1f0(x)h(x))=2f0(x)+f02(x)h(x)

多项式开方,除法等也可用此方法推出,不过咕咕咕。

多项式对数函数

这个东西不需要牛顿迭代,只是 exp 的前置知识。

给定 f(x),求 ln(f(x))

根据定义,若 ln(f(x)) 存在,则必须满足:

[x0]f(x)=1

ln(f(x)) 求导再积分,得:

ln(f(x))=f(x)f(x)dx

#include<cstdio> #include<iostream> #define LL long long using namespace std; inline LL read() { LL res = 0, f = 1; char ch; while((ch = getchar()) && (ch < '0' || ch > '9') && ch != '-'); (ch == '-') ? f = -1 : res = ch - '0'; while((ch = getchar()) && ch >= '0' && ch <= '9') res = (res << 3) + (res << 1) + ch - '0'; return res * f; } int prnum[21]; inline void print(LL x) { if(x == 0) putchar('0'); else { register int prcnt = 0; while(x) { prnum[++prcnt] = x % 10; x /= 10; } for(register int i = prcnt; i >= 1; i--) putchar(prnum[i] + '0'); } } const int MAXN = 4e5 + 5; const LL MOD = 998244353, G = 3, Gi = 332748118; int n, rev[MAXN]; LL f[MAXN], g[MAXN], c[MAXN]; inline LL expow(LL x, LL y) { LL res = 1; while(y) { if(y & 1) res = res * x % MOD; y >>= 1, x = x * x % MOD; } return res; } inline void NTT(LL *A, int lim, int op) { for(int i = 0; i < lim; i++) if(i > rev[i]) swap(A[i], A[rev[i]]); for(int mid = 1; mid < lim; mid <<= 1) { LL wn = expow((op == 1) ? G : Gi, (MOD - 1) / (mid << 1)); for(int r = (mid << 1), j = 0; j < lim; j += r) { LL w = 1; for(int k = 0; k < mid; k++, w = w * wn % MOD) { LL x = A[j + k], y = w * A[j + k + mid] % MOD; A[j + k] = (x + y) % MOD, A[j + k + mid] = ((x - y) % MOD + MOD) % MOD; } } } } void getinv(int len, LL *a, LL *b) { if(len == 1) return b[0] = expow(a[0], MOD - 2), void(); getinv((len + 1) >> 1, a, b); int lim = 1, tot = 0; while(lim < (len << 1)) lim <<= 1, ++tot; for(int i = 1; i < lim; i++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (tot - 1)); for(int i = 0; i < len; i++) c[i] = a[i]; for(int i = len; i < lim; i++) c[i] = 0; NTT(c, lim, 1); NTT(b, lim, 1); for(int i = 0; i < lim; i++) b[i] = ((2 - c[i] * b[i]) % MOD * b[i] % MOD + MOD) % MOD; NTT(b, lim, -1); LL inv = expow(lim, MOD - 2); for(int i = 0; i < len; i++) b[i] = b[i] * inv % MOD; for(int i = len; i < lim; i++) b[i] = 0; } void getln(int len, LL *a, LL *b) { int lim = 1, tot = 0; while(lim < (len << 1)) lim <<= 1, ++tot; for(int i = 1; i < lim; i++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (tot - 1)); for(int i = 1; i < len; i++) c[i - 1] = a[i] * i % MOD; c[len - 1] = 0; for(int i = len; i < lim; i++) c[i] = 0; NTT(c, lim, 1); NTT(b, lim, 1); for(int i = 0; i < lim; i++) b[i] = c[i] * b[i] % MOD; NTT(b, lim, -1); LL inv = expow(lim, MOD - 2); for(int i = len - 1; i >= 1; i--) b[i] = b[i - 1] * inv % MOD * expow(i, MOD - 2) % MOD; b[0] = 0; for(int i = len; i < lim; i++) b[i] = 0; } int main() { n = read(); for(int i = 0; i < n; i++) f[i] = read(); getinv(n, f, g); getln(n, f, g); for(int i = 0; i < n; i++) print(g[i]), putchar(' '); return 0; }

多项式指数函数

给定 h(x),求 f(x)=eh(x)

g(f(x))=ln(f(x))h(x),所以

f(x)=f0(x)ln(f0(x))h(x)1f0(x)=f0(x)f0(x)(ln(f0(x))h(x))=f0(x)(1ln(f0(x))+h(x))

#include<cstdio> #include<iostream> #define LL long long using namespace std; inline LL read() { LL res = 0, f = 1; char ch; while((ch = getchar()) && (ch < '0' || ch > '9') && ch != '-'); (ch == '-') ? f = -1 : res = ch - '0'; while((ch = getchar()) && ch >= '0' && ch <= '9') res = (res << 3) + (res << 1) + ch - '0'; return res * f; } int prnum[21]; inline void print(LL x) { if(x == 0) putchar('0'); else { register int prcnt = 0; while(x) { prnum[++prcnt] = x % 10; x /= 10; } for(register int i = prcnt; i >= 1; i--) putchar(prnum[i] + '0'); } } const int MAXN = 4e5 + 5; const LL MOD = 998244353, G = 3, Gi = 332748118; int n, rev[MAXN]; LL f[MAXN], g[MAXN], h[MAXN], c[MAXN]; inline LL expow(LL x, LL y) { LL res = 1; while(y) { if(y & 1) res = res * x % MOD; y >>= 1, x = x * x % MOD; } return res; } inline void NTT(LL *A, int lim, int op) { for(int i = 1; i < lim; i++) if(i > rev[i]) swap(A[i], A[rev[i]]); for(int mid = 1; mid < lim; mid <<= 1) { LL wn = expow((op == 1) ? G : Gi, (MOD - 1) / (mid << 1)); for(int r = (mid << 1), j = 0; j < lim; j += r) { LL w = 1; for(int k = 0; k < mid; k++, w = w * wn % MOD) { LL x = A[j + k], y = w * A[j + k + mid] % MOD; A[j + k] = (x + y) % MOD, A[j + k + mid] = ((x - y) % MOD + MOD) % MOD; } } } } void getinv(int len, LL *a, LL *b) { if(len == 1) return b[0] = expow(a[0], MOD - 2), void(); getinv((len + 1) >> 1, a, b); int lim = 1, tot = 0; while(lim < (len << 1)) lim <<= 1, ++tot; for(int i = 1; i < lim; i++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (tot - 1)); for(int i = 0; i < len; i++) c[i] = a[i]; for(int i = len; i < lim; i++) c[i] = 0; NTT(c, lim, 1); NTT(b, lim, 1); for(int i = 0; i < lim; i++) b[i] = ((2 - b[i] * c[i]) % MOD * b[i] % MOD + MOD) % MOD; NTT(b, lim, -1); LL inv = expow(lim, MOD - 2); for(int i = 0; i < len; i++) b[i] = b[i] * inv % MOD; for(int i = len; i < lim; i++) b[i] = 0; } void getln(int len, LL *a, LL *b) { int lim = 1, tot = 0; while(lim < (len << 1)) lim <<= 1, ++tot; for(int i = 1; i < lim; i++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (tot - 1)); for(int i = 1; i < len; i++) c[i - 1] = a[i] * i % MOD; c[len - 1] = 0; for(int i = len; i < lim; i++) c[i] = 0; NTT(c, lim, 1); NTT(b, lim, 1); for(int i = 0; i < lim; i++) b[i] = c[i] * b[i] % MOD; NTT(b, lim, -1); LL inv = expow(lim, MOD - 2); for(int i = len - 1; i >= 1; i--) b[i] = b[i - 1] * inv % MOD * expow(i, MOD - 2) % MOD; b[0] = 0; for(int i = len; i < lim; i++) b[i] = 0; } void getexp(int len, LL *a, LL *b) { if(len == 1) return b[0] = 1, void(); getexp((len + 1) >> 1, a, b); int lim = 1, tot = 0; while(lim < (len << 1)) lim <<= 1, ++tot; for(int i = 0; i < lim; i++) h[i] = 0; getinv(len, b, h); getln(len, b, h); for(int i = 1; i < lim; i++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (tot - 1)); h[0] = ((1 - h[0] + a[0]) % MOD + MOD) % MOD; for(int i = 1; i < len; i++) h[i] = ((a[i] - h[i]) % MOD + MOD) % MOD; NTT(h, lim, 1); NTT(b, lim, 1); for(int i = 0; i < lim; i++) b[i] = b[i] * h[i] % MOD; NTT(b, lim, -1); LL inv = expow(lim, MOD - 2); for(int i = 0; i < len; i++) b[i] = b[i] * inv % MOD; for(int i = len; i < lim; i++) b[i] = 0; } int main() { n = read(); for(int i = 0; i < n; i++) f[i] = read(); getexp(n, f, g); for(int i = 0; i < n; i++) print(g[i]), putchar(' '); return 0; }

__EOF__

本文作者zym417
本文链接https://www.cnblogs.com/zym417/p/15831852.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zym417  阅读(55)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示