多项式全家桶

多项式乘法

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\) 使得 \(F*G \equiv 1\ ( mod\ x^n )\)
\(F*H \equiv 1\ (mod\ x^{\lceil \frac{n}{2} \rceil} )\)

\[G-H \equiv 0\ (mod\ x^{\lceil \frac{n}{2} \rceil} ) \]

\[G^2 - 2G*H + H^2 \equiv 0\ (mod\ x^n) \]

两边同乘 \(F\),得:

\[G-2H+F*H^2 \equiv 0\ (mod\ x^n) \]

\[G\equiv (2-F*H)*H\ (mod\ x^n) \]

当递归到常数项时,\(H_0\)\(F_0\) 在模 \(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\) 使得 \(G^2 \equiv F\ (mod\ x^n)\)
\(H^2 \equiv F\ (mod\ x^{\lceil \frac{n}{2} \rceil})\)

\[G-H \equiv 0\ (mod\ x^{\lceil \frac{n}{2} \rceil}) \]

\[G^2 - 2G*H + H^2 \equiv 0\ (mod\ x^n) \]

\[F - 2G*H + H^2 \equiv 0\ (mod\ x^n) \]

\[G \equiv \frac{1}{2}(F*H^{-1}+H)\ (mod\ x^n) \]

当递归到常数项时,令 \(H_0\)\(F_0\) 在模 \(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)\equiv G(x)*Q(x) + R(x)\ (mod\ x^n)\quad (F(x) 共 n 项,G(x) 共 m 项)\)
若消除 \(R(x)\) 的影响,则可以直接通过多项式乘法逆求出 \(G(x)\),再直接代入,即可求出 \(R(x)\)
\(A_R(x)=x^{deg F}A(\frac{1}{x})\)

\[F(\frac{1}{x})\equiv G(\frac{1}{x})*Q(\frac{1}{x}) + R(\frac{1}{x})\ (mod\ x^n) \]

\[x^nF(\frac{1}{x})\equiv x^mG(\frac{1}{x})x^{n-m}Q(\frac{1}{x}) + x^nR(\frac{1}{x})\ (mod\ x^n) \]

\[F_R(x)\equiv G_R(x)*Q_R(x)+x^{n-m+1}R_R(x)\ (mod\ x^n) \]

\[F_R(x)\equiv G_R(x)*Q_R(x)\ (mod\ x^{n-m+1}) \]

因为 \(Q(x)\) 仅有 \(n-m\) 项,所以模 \(x^{n-m+1}\) 并不影响。

\[Q_R(x)\equiv F_R(x)*G_R^{-1}(x) \]

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

\[R(x)\equiv 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)=\sum_i a_ix^i\),则

\[\frac{df(x)}{dx}=f'(x)=\sum_i(i+1)a_{i+1}x^i \]

\[\int f(x) dx = \sum_{i > 0}\frac{a_{i-1}}{i}x^i \]

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

\[\sum^{\infty}_{i=0}\frac{d^if}{dx}(a)\frac{(x-a)^i}{i!} \]

描述

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

\[g(f(x)) \equiv 0 \ (mod\ x^n) \]

求在模 \(x^n\) 意义下的 \(f(x)\)

牛顿迭代

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

\(f(x)\) 在模 \(x^{\lceil \frac{n}{2} \rceil}\) 意义下的解为 \(f_0(x)\),要求在模 \(x^n\) 意义下的解 \(f(x)\)

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

\[\sum^{\infty}_{i=0}\frac{d^ig}{dx}(f_0(x))\frac{(f(x)-f_0(x))^i}{i!} \]

因为 \(f(x)-f_0(x)\) 的最低非零项次数为 \(\lceil \frac{n}{2} \rceil\),所以

\[(f(x)-f_0(x))^i \equiv 0\ (mod\ x^n) \quad (i \geq 2) \]

所以

\[\sum^{\infty}_{i=0}\frac{d^ig}{dx}(f_0(x))\frac{(f(x)-f_0(x))^i}{i!} = g(f_0(x))-g'(f(x)-f_0(x)) \]

所以

\[f(x)=f_0(x)- \frac{g(f_0(x))}{g'(f_0(x))} \]

应用

多项式求逆

给定 \(h(x)\),求 \(f(x)=h^{-1}(x)\)

\(g(f(x))=\frac{1}{f(x)}-h(x)\),所以

\[\begin{align*} f(x) & = f_0(x)-\frac{\frac{1}{f_0(x)}-h(x)}{-\frac{1}{f_0^2(x)}}\\ & = f_0(x)+f_0^2(x)(\frac{1}{f_0(x)}-h(x))\\ & = 2f_0(x)+f_0^2(x)h(x) \end{align*}\]

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

多项式对数函数

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

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

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

\[[x^0]f(x)=1 \]

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

\[ln(f(x)) = \int \frac{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)=e^{h(x)}\)

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

\[\begin{align*} f(x) & = f_0(x)-\frac{\ln(f_0(x))-h(x)}{\frac{1}{f_0(x)}}\\ & = f_0(x)-f_0(x)(\ln(f_0(x))-h(x))\\ & = f_0(x)(1- \ln(f_0(x))+h(x)) \end{align*}\]

#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;
}
posted @ 2022-01-21 21:29  zym417  阅读(54)  评论(0编辑  收藏  举报