Loading

【笔记】从零开始的多项式学习

【模板】多项式乘法

板子,基础。

#define N 2097160
inline int ck(int x){return x >= P ? x - P : x; }
int t, rev[N];
void ntt(int *a,int op){
	rp(i, t - 1)if(rev[i] < i)swap(a[i], a[rev[i]]);
	for(int k = 2, l = 1; k <= t; k <<= 1, l <<= 1){
		int g = Pow(op ? 3 : Q, (P - 1) / k);
		for(int i = 0; i < t; i += k){
			LL cur = 1;
			rep(j, 0, l - 1){
				int x = a[i + j], y = a[i + l + j] * cur % P;
				a[i + j] = ck(x + y), a[i + l + j] = ck(x - y + P);
				cur = cur * g % P;
			}
		}
	}
}
void mul(int *u,int *v,int *w,int n,int m){
	t = 1; while(t <= n + m)t <<= 1;
	rp(i, t - 1)rev[i] = (rev[i >> 1] >> 1) | ((i & 1) ? (t >> 1) : 0);
	ntt(u, 1), ntt(v, 1);
	rep(i, 0, t)w[i] = u[i] * (LL)v[i] % P;
	ntt(w, 0); LL iv = Pow(t, P - 2);
	rep(i, 0, t)w[i] = w[i] * iv % P;
}
int n, m, u[N], v[N], w[N];
int main() {
	read(n, m);
	rep(i, 0, n)read(u[i]);
	rep(i, 0, m)read(v[i]);
	mul(u, v, w, n, m);
	rep(i, 0, n + m)printf("%d ", ck(w[i] + P)); el;
	return 0;
}

【模板】多项式求逆

倍增解决。

#define N 270005
int rev[N], t;
inline int ck(int x){return x >= P ? x - P : x; }
void ntt(int *a,int op){
	rp(i, t - 1)if(rev[i] < i)swap(a[i], a[rev[i]]);
	for(int k = 2, l = 1; k <= t; k <<= 1, l <<= 1){
		int g = Pow(op ? 3 : Q, (P - 1) / k);
		for(int i = 0; i < t; i += k){
			LL cur = 1;
			rep(j, 0, l - 1){
				int x = a[i + j], y = a[i + l + j] * cur % P;
				a[i + j] = ck(x + y), a[i + l + j] = ck(x - y + P);
				cur = cur * g % P;
			}
		}
	}
}
int p[N];
void solve(int *u,int *v,int n){
	if(1 == n){	v[0] = Pow(u[0], P - 2); return ;}
	int m = (n + 1) / 2;
	solve(u, v, m), t = 1;
	while(t <= n + n)t <<= 1;
	rp(i, t - 1)rev[i] = (rev[i >> 1] >> 1) | ((i & 1) ? (t >> 1) : 0);
	rep(i, 0, t - 1)p[i] = 0;
	rep(i, 0, n - 1)p[i] = u[i];
	ntt(p, 1), ntt(v, 1);
	rep(i, 0, t - 1)v[i] = v[i] * (2 - p[i] * (LL)v[i] % P + P) % P;
	ntt(v, 0); LL iv = Pow(t, P - 2);
	rep(i, 0, n - 1)v[i] = v[i] * iv % P;
	rep(i, n, t - 1)v[i] = 0;
}
int n, a[N], b[N];
int main() {
	read(n);
	rep(i, 0, n - 1)read(a[i]);
	solve(a, b, n);
	rep(i, 0, n - 1)printf("%d ", b[i]);
	return 0;
}

【模板】多项式对数函数

\(\ln(F(x))\),先求导再积分,得到 \(\ln(F(x)) = \int \dfrac{F'(x)}{F(x)}\),套一个多项式求逆板子即可。

#define N 270005
int rev[N], t;
inline int ck(int x){return x >= P ? x - P : x; }
void ntt(int *a,int op){
	rp(i, t - 1)if(rev[i] < i)swap(a[i], a[rev[i]]);
	for(int k = 2, l = 1; k <= t; k <<= 1, l <<= 1){
		int g = Pow(op ? 3 : Q, (P - 1) / k);
		for(int i = 0; i < t; i += k){
			LL cur = 1;
			rep(j, 0, l - 1){
				int x = a[i + j], y = a[i + l + j] * cur % P;
				a[i + j] = ck(x + y), a[i + l + j] = ck(x - y + P);
				cur = cur * g % P;
			}
		}
	}
}
void mul(int *u,int *v,int *w,int lm){
	t = 1; while(t <= lm)t <<= 1;
	rp(i, t - 1)rev[i] = (rev[i >> 1] >> 1) | ((i & 1) ? (t >> 1) : 0);
	ntt(u, 1), ntt(v, 1);
	rep(i, 0, t)w[i] = u[i] * (LL)v[i] % P;
	ntt(w, 0); LL iv = Pow(t, P - 2);
	rep(i, 0, t)w[i] = w[i] * iv % P;
}
int p[N];
void solve(int *u,int *v,int n){
	if(1 == n){	v[0] = Pow(u[0], P - 2); return ;}
	int m = (n + 1) / 2;
	solve(u, v, m), t = 1;
	while(t <= n + n)t <<= 1;
	rp(i, t - 1)rev[i] = (rev[i >> 1] >> 1) | ((i & 1) ? (t >> 1) : 0);
	rep(i, 0, t - 1)p[i] = 0;
	rep(i, 0, n - 1)p[i] = u[i];
	ntt(p, 1), ntt(v, 1);
	rep(i, 0, t - 1)v[i] = v[i] * (2 - p[i] * (LL)v[i] % P + P) % P;
	ntt(v, 0); LL iv = Pow(t, P - 2);
	rep(i, 0, n - 1)v[i] = v[i] * iv % P;
	rep(i, n, t - 1)v[i] = 0;
}
int n, a[N], b[N], c[N], g[N];
int main() {
	read(n);
	rep(i, 0, n - 1)read(a[i]);
	solve(a, b, n);
	rp(i, n - 1)c[i - 1] = a[i] * (LL)i % P;
	mul(b, c, g, n + n - 2);
	pc('0'), pc(' ');
	rp(i, n - 1)printf("%lld ", g[i - 1] * (LL)Pow(i, P - 2) % P);
	return 0;
}

泰勒展开

对于可导函数 \(f\),有

\[f(x) = \sum\limits_{i \ge 0}\frac{f^{(i)}(x_0)}{i!}(x - x_0)^i \]

牛顿迭代

已知多项式 \(g(x)\),和条件:

\[g(f(x))\equiv 0\pmod {x^n} \]

\(m = \left\lceil\frac{n + 1}{2}\right\rceil\),我们求出在 \(\bmod x^m\) 意义下的解 \(f_0(x)\),然后再 \(f_0(x)\) 处泰勒展开。由于 \(f(x)\)\(f_0(x)\) 最低 \(m\) 项是相同的,所以在 \(i\ge 2\)\((f(x) - f_0(x))^i \equiv 0\pmod{x^n}\)。所以我们得到:

\[f(x)\equiv f_0(x) - \dfrac{g(f_0(x))}{g'(f_0(x))}\pmod{x^n} \]

【模板】多项式指数函数

\(G(x) = \exp(F(x))\),构造函数 \(g(f(x)) = \ln f(x) - F(x)\),有 \(g(G(x)) \equiv0\),直接牛顿迭代得到 \(G(x)\equiv G_0(x) - \dfrac{\ln(G_0(x)) - F(x)}{1/(G_0(x))}\),化简后得到 :

\[G(x) = G_0(x)(1-\ln G_0(x) + F(x)) \]

倍增即可,套一个多项式 ln 的板子。

#define N 270005
int rev[N], t;
inline int ck(int x){return x >= P ? x - P : x; }
void ntt(int *a,int op){
	rp(i, t - 1)if(rev[i] < i)swap(a[i], a[rev[i]]);
	for(int k = 2, l = 1; k <= t; k <<= 1, l <<= 1){
		int g = Pow(op ? 3 : Q, (P - 1) / k);
		for(int i = 0; i < t; i += k){
			LL cur = 1;
			rep(j, 0, l - 1){
				int x = a[i + j], y = a[i + l + j] * cur % P;
				a[i + j] = ck(x + y), a[i + l + j] = ck(x - y + P);
				cur = cur * g % P;
			}
		}
	}
}
void mul(int *u,int *v,int *w,int lm){
	t = 1; while(t <= lm)t <<= 1;
	rp(i, t - 1)rev[i] = (rev[i >> 1] >> 1) | ((i & 1) ? (t >> 1) : 0);
	ntt(u, 1), ntt(v, 1);
	rep(i, 0, t)w[i] = u[i] * (LL)v[i] % P;
	ntt(w, 0); LL iv = Pow(t, P - 2);
	rep(i, 0, t)w[i] = w[i] * iv % P;
}
int p[N];
void solve(int *u,int *v,int n){
	if(1 == n){	v[0] = Pow(u[0], P - 2); return ;}
	int m = (n + 1) / 2;
	solve(u, v, m), t = 1;
	while(t <= n + n)t <<= 1;
	rp(i, t - 1)rev[i] = (rev[i >> 1] >> 1) | ((i & 1) ? (t >> 1) : 0);
	rep(i, 0, t - 1)p[i] = 0;
	rep(i, 0, n - 1)p[i] = u[i];
	ntt(p, 1), ntt(v, 1);
	rep(i, 0, t - 1)v[i] = v[i] * (2 - p[i] * (LL)v[i] % P + P) % P;
	ntt(v, 0); LL iv = Pow(t, P - 2);
	rep(i, 0, n - 1)v[i] = v[i] * iv % P;
	rep(i, n, t - 1)v[i] = 0;
}
int b[N], c[N], iv[N];
void polyln(int *a,int *g,int n){
	t = 1; while(t <= n + n)t <<= 1;
	rep(i, 0, t - 1)b[i] = c[i] = 0;
	solve(a, b, n);
	rp(i, n - 1)c[i - 1] = a[i] * (LL)i % P;
	mul(b, c, g, n + n - 2);
	pr(i, n - 1)g[i] = g[i - 1] * (LL)iv[i] % P;
	g[0] = 0;
}
int w[N], f[N];
void polyexp(int *a,int *u,int n){
	if(1 == n){u[0] = 1; return;}
	int m = (n + 1) / 2;
	polyexp(a, u, m), polyln(u, f, n);
	rep(i, 0, n - 1)f[i] = ck(a[i] + (i == 0) - f[i] + P);
	mul(f, u, w, n + n - 2);
	rep(i, 0, n - 1)u[i] = w[i];
	rep(i, n, t - 1)u[i] = 0;
}
int n, a[N], g[N];
int main() {
	read(n);
	rep(i, 0, n - 1)read(a[i]);
	iv[1] = 1; rep(i, 2, n)iv[i] = P - (P / i) * (LL)iv[P % i] % P;
	polyexp(a, g, n);
	rep(i, 0, n - 1)printf("%d ", g[i]);
	return 0;
}

【模板】多项式快速幂

\(F^k(x)\),取多项式 \(\ln\) 得到 \(\ln F^k(x) = k\ln F(x)\),然后再 \(\exp\) 回去即可。

#define N 270005
int rev[N], t;
inline int ck(int x){return x >= P ? x - P : x; }
void ntt(int *a,int op){
	rp(i, t - 1)if(rev[i] < i)swap(a[i], a[rev[i]]);
	for(int k = 2, l = 1; k <= t; k <<= 1, l <<= 1){
		int g = Pow(op ? 3 : Q, (P - 1) / k);
		for(int i = 0; i < t; i += k){
			LL cur = 1;
			rep(j, 0, l - 1){
				int x = a[i + j], y = a[i + l + j] * cur % P;
				a[i + j] = ck(x + y), a[i + l + j] = ck(x - y + P);
				cur = cur * g % P;
			}
		}
	}
}
void mul(int *u,int *v,int *w,int lm){
	t = 1; while(t <= lm)t <<= 1;
	rp(i, t - 1)rev[i] = (rev[i >> 1] >> 1) | ((i & 1) ? (t >> 1) : 0);
	ntt(u, 1), ntt(v, 1);
	rep(i, 0, t)w[i] = u[i] * (LL)v[i] % P;
	ntt(w, 0); LL iv = Pow(t, P - 2);
	rep(i, 0, t)w[i] = w[i] * iv % P;
}
int p[N];
void solve(int *u,int *v,int n){
	if(1 == n){	v[0] = Pow(u[0], P - 2); return ;}
	int m = (n + 1) / 2;
	solve(u, v, m), t = 1;
	while(t <= n + n)t <<= 1;
	rp(i, t - 1)rev[i] = (rev[i >> 1] >> 1) | ((i & 1) ? (t >> 1) : 0);
	rep(i, 0, t - 1)p[i] = 0;
	rep(i, 0, n - 1)p[i] = u[i];
	ntt(p, 1), ntt(v, 1);
	rep(i, 0, t - 1)v[i] = v[i] * (2 - p[i] * (LL)v[i] % P + P) % P;
	ntt(v, 0); LL iv = Pow(t, P - 2);
	rep(i, 0, n - 1)v[i] = v[i] * iv % P;
	rep(i, n, t - 1)v[i] = 0;
}
int b[N], c[N], iv[N];
void polyln(int *a,int *g,int n){
	t = 1; while(t <= n + n)t <<= 1;
	rep(i, 0, t - 1)b[i] = c[i] = 0;
	solve(a, b, n);
	rp(i, n - 1)c[i - 1] = a[i] * (LL)i % P;
	mul(b, c, g, n + n - 2);
	pr(i, n - 1)g[i] = g[i - 1] * (LL)iv[i] % P;
	g[0] = 0;
}
int w[N], f[N];
void polyexp(int *a,int *u,int n){
	if(1 == n){u[0] = 1; return;}
	int m = (n + 1) / 2;
	polyexp(a, u, m), polyln(u, f, n);
	rep(i, 0, n - 1)f[i] = ck(a[i] + (i == 0) - f[i] + P);
	mul(f, u, w, n + n - 2);
	rep(i, 0, n - 1)u[i] = w[i];
	rep(i, n, t - 1)u[i] = 0;
}
int n, k, a[N], g[N], h[N];
int main() {
	read(n, k);
	rep(i, 0, n - 1)read(a[i]);
	iv[1] = 1; rep(i, 2, n)iv[i] = P - (P / i) * (LL)iv[P % i] % P;
	polyln(a, h, n);
	rep(i, 0, n - 1)h[i] = h[i] * (LL)k % P;
	polyexp(h, g, n);
	rep(i, 0, n - 1)printf("%d ", g[i]);
	return 0;
}

【模板】多项式开根

构造 \(g(f(x)) = f^2(x) - A(x)\),有 \(g(B(x)) \equiv 0\),直接牛顿迭代即可,套一个多项式求逆的板子。

\[B(x) = \dfrac{B_0^2(x) + A(x)}{2B_0(x)} \]

#define N 270005
int rev[N], t;
inline int ck(int x){return x >= P ? x - P : x; }
void ntt(int *a,int op){
	rp(i, t - 1)if(rev[i] < i)swap(a[i], a[rev[i]]);
	for(int k = 2, l = 1; k <= t; k <<= 1, l <<= 1){
		int g = Pow(op ? 3 : Q, (P - 1) / k);
		for(int i = 0; i < t; i += k){
			LL cur = 1;
			rep(j, 0, l - 1){
				int x = a[i + j], y = a[i + l + j] * cur % P;
				a[i + j] = ck(x + y), a[i + l + j] = ck(x - y + P);
				cur = cur * g % P;
			}
		}
	}
}
void mul(int *u,int *v,int *w,int lm){
	t = 1; while(t <= lm)t <<= 1;
	rp(i, t - 1)rev[i] = (rev[i >> 1] >> 1) | ((i & 1) ? (t >> 1) : 0);
	ntt(u, 1), ntt(v, 1);
	rep(i, 0, t)w[i] = u[i] * (LL)v[i] % P;
	ntt(w, 0); LL iv = Pow(t, P - 2);
	rep(i, 0, t)w[i] = w[i] * iv % P;
}
int p[N];
void solve(int *u,int *v,int n){
	if(1 == n){	v[0] = Pow(u[0], P - 2); return ;}
	int m = (n + 1) / 2;
	solve(u, v, m), t = 1;
	while(t <= n + n)t <<= 1;
	rp(i, t - 1)rev[i] = (rev[i >> 1] >> 1) | ((i & 1) ? (t >> 1) : 0);
	rep(i, 0, t - 1)p[i] = 0;
	rep(i, 0, n - 1)p[i] = u[i];
	ntt(p, 1), ntt(v, 1);
	rep(i, 0, t - 1)v[i] = v[i] * (2 - p[i] * (LL)v[i] % P + P) % P;
	ntt(v, 0); LL iv = Pow(t, P - 2);
	rep(i, 0, n - 1)v[i] = v[i] * iv % P;
	rep(i, n, t - 1)v[i] = 0;
}
int l[N], c[N], d[N];
void sqr(int *u,int *v,int n){
	if(1 == n){v[0] = 1; return ;}
	int m = (n + 1) / 2;
	sqr(u, v, m), t = 1;
	while(t <= n + n)t <<= 1;
	rep(i, 0, t - 1)l[i] = c[i] = d[i] = 0;
	rep(i, 0, n - 1)l[i] = v[i];
	solve(v, c, n), 
	mul(l, v, d, n + n);
	rep(i, 0, n - 1)ad(d[i], u[i]);
	rep(i, n, t - 1)d[i] = 0;
	mul(d, c, v, n + n);
	rep(i, 0, n - 1)v[i] = v[i] * (LL)I % P;
	rep(i, n, t - 1)v[i] = 0;
}
int n, a[N], b[N];
int main() {
	read(n);
	rep(i, 0, n - 1)read(a[i]);
	sqr(a, b, n);
	rep(i, 0, n - 1)printf("%d ", b[i]);
	return 0;
}
posted @ 2022-08-29 11:46  7KByte  阅读(225)  评论(2编辑  收藏  举报