多项式卷积
FFT
(原理先咕着)
精度炸裂
#include <bits/stdc++.h>
using namespace std;
#define in Read()
inline int in {
int s = 0, f = 1;
char x;
for (x = getchar(); x < '0' || x > '9'; x = getchar())
if (x == '-') f = -1;
for (; x >= '0' && x <= '9'; x = getchar()) s = (s * 10) + (x & 15);
return f == 1 ? s : -s;
}
const int A = 5e6 + 5;
const double pi = acos(-1);
int n, m;
int las, num;
int r[A];
struct node {
double x, y;
node(double u = 0, double v = 0) { x = u, y = v; }
inline friend node operator+(const node u, const node v) {
return node(u.x + v.x, u.y + v.y);
}
inline friend node operator-(const node u, const node v) {
return node(u.x - v.x, u.y - v.y);
}
inline friend node operator*(const node u, const node v) {
return node(u.x * v.x - u.y * v.y, u.x * v.y + u.y * v.x);
}
} a[A], b[A];
inline void fft(node *Q, double inv) {
for (int i = 0; i < las; i++)
if (i < r[i]) swap(Q[i], Q[r[i]]);
for (int mid = 1; mid < las; mid <<= 1) {
node wn = node(cos(pi / mid), sin(pi / mid) * inv);
for (int i = 0; i < las; i += mid << 1) {
node fw = node(1, 0);
for (int j = 0; j < mid; j++, fw = fw * wn) {
node u = Q[i + j], v = Q[i + j + mid] * fw;
Q[i + j] = u + v;
Q[i + j + mid] = u - v;
}
}
}
return;
}
inline void fold() {
las = 1, num = 0;
while (las <= n + m) las <<= 1, num++;
for (int i = 0; i < las; i++)
r[i] = (r[i >> 1] >> 1) | ((i & 1) << (num - 1));
fft(a, 1), fft(b, 1);
for (int i = 0; i < las; i++) a[i] = a[i] * b[i];
fft(a, -1);
for (int i = 0; i <= n + m; i++) a[i].x = (int)(a[i].x / las + 0.5);
return;
}
signed main() {
n = in, m = in;
for (int i = 0; i <= n; i++) a[i].x = in;
for (int i = 0; i <= m; i++) b[i].x = in;
fold();
for (int i = 0; i <= n + m; i++) printf("%d ", (int)a[i].x);
return 0;
}
NTT
将 fft 中的复平面单位根换成了原根
质数:998244353 原根:3
#include <bits/stdc++.h>
using namespace std;
#define in Read()
#define int long long
inline int in {
int s = 0, f = 1;
char x;
for (x = getchar(); x < '0' || x > '9'; x = getchar())
if (x == '-') f = -1;
for (; x >= '0' && x <= '9'; x = getchar()) s = (s * 10) + (x & 15);
return f == 1 ? s : -s;
}
const int A = 5e6 + 5;
const int mod = 998244353;
int n, m, r[A];
int a[A], b[A];
int lim;
inline int quick(int s, int c) {
int ans = 1;
while (c) {
if (c & 1) ans = ans * s % mod;
s = s * s % mod;
c >>= 1;
}
return ans;
}
inline void ntt(int *Q, int pos) {
for (int i = 0; i < lim; i++)
if (i < r[i]) swap(Q[i], Q[r[i]]);
for (int mid = 1; mid < lim; mid <<= 1) {
int wn = quick(3, (mod - 1) / (mid << 1));
for (int i = 0; i < lim; i += (mid << 1)) {
int fw = 1;
for (int j = 0; j < mid; j++, fw = fw * wn % mod) {
int x = Q[i + j], y = Q[i + j + mid] * fw % mod;
Q[i + j] = (x + y) % mod;
Q[i + j + mid] = (x - y + mod) % mod;
}
}
}
if (pos) return;
int inv = quick(lim, mod - 2);
reverse(Q + 1, Q + lim);
for (int i = 0; i < lim; i++) Q[i] = Q[i] * inv % mod;
return;
}
inline void fold(int *x, int *y, int la, int lb) {
lim = 1;
while (lim < la + lb) lim <<= 1;
for (int i = 0; i < lim; i++)
r[i] = (r[i >> 1] >> 1) | ((i & 1) ? lim >> 1 : 0);
ntt(a, 1), ntt(b, 1);
for (int i = 0; i < lim; i++) a[i] = a[i] * b[i] % mod;
ntt(a, 0);
return;
}
signed main() {
n = in, m = in;
n++, m++;
for (int i = 0; i < n; i++) a[i] = in;
for (int i = 0; i < m; i++) b[i] = in;
fold(a, b, n, m);
for (int i = 0; i < n + m - 1; i++) printf("%lld ", a[i]);
return 0;
}
分治NTT
用 cdq 分治的思路处理
#include <bits/stdc++.h>
using namespace std;
#define in Read()
#define int long long
inline int in {
int s = 0, f = 1;
char x;
for (x = getchar(); x < '0' || x > '9'; x = getchar())
if (x == '-') f = -1;
for (; x >= '0' && x <= '9'; x = getchar()) s = (s * 10) + (x & 15);
return f == 1 ? s : -s;
}
const int A = 4e5 + 5;
const int mod = 998244353;
int lim, r[A];
inline int power(int s, int c) {
int ans = 1;
while (c) {
if (c & 1) ans = ans * s % mod;
s = s * s % mod;
c >>= 1;
}
return ans;
}
inline void init(int len) {
lim = 1;
while (lim < len) lim <<= 1;
for (int i = 1; i < lim; i++)
r[i] = (r[i >> 1] >> 1) | ((i & 1) ? lim >> 1 : 0);
return;
}
inline void ntt(int *Q, int pos) {
for (int i = 0; i < lim; i++)
if (i < r[i]) swap(Q[i], Q[r[i]]);
for (int mid = 1; mid < lim; mid <<= 1) {
int wn = power(3, (mod - 1) / (mid << 1));
for (int i = 0; i < lim; i += (mid << 1)) {
int fw = 1;
for (int j = 0; j < mid; j++, fw = fw * wn % mod) {
int x = Q[i + j], y = Q[i + j + mid] * fw % mod;
Q[i + j] = (x + y) % mod;
Q[i + j + mid] = (x - y + mod) % mod;
}
}
}
if (pos) return;
int k = power(lim, mod - 2);
reverse(Q + 1, Q + lim);
for (int i = 0; i < lim; i++) Q[i] = Q[i] * k % mod;
return;
}
inline void Flow(int *x, int *y, int lx, int ly) {
init(lx + ly);
ntt(x, 1), ntt(y, 1);
for (int i = 0; i < lim; i++) x[i] = x[i] * y[i] % mod;
ntt(x, 0);
return;
}
int n;
int a[A], b[A];
int p1_ntt[A], p2_ntt[A];
inline void solve(int *x, int *y, int l, int r) {
#define p1 p1_ntt
#define p2 p2_ntt
if (l == r) {
if (!l) y[0] = 1;
return;
}
int mid = (l + r) >> 1;
solve(x, y, l, mid);
for (int i = 0; i < lim; i++) p1[i] = p2[i] = 0;
for (int i = l; i <= mid; i++) p1[i - l] = y[i];
for (int i = 0; i <= r - l; i++) p2[i] = x[i];
Flow(p1, p2, r - l + 1, 0);
for (int i = mid + 1; i <= r; i++) y[i] = (y[i] + p1[i - l]) % mod;
solve(x, y, mid + 1, r);
#undef p1
#undef p2
}
signed main() {
n = in;
for (int i = 1; i < n; i++) a[i] = in;
solve(a, b, 0, n - 1);
for (int i = 0; i < n; i++) printf("%lld ", b[i]);
puts("");
return 0;
}
多项式求逆
A
×
B
≡
1
(
m
o
d
x
n
)
A\times B \equiv 1(mod x^n)
A×B≡1(modxn)
知 A 求 B
有
A
×
B
≡
1
(
m
o
d
x
n
)
假
设
已
知
B
′
A
×
B
′
≡
1
(
m
o
d
x
n
2
)
B
−
B
′
≡
0
(
m
o
d
x
n
2
)
(
B
−
B
′
)
2
≡
0
(
m
o
d
x
n
)
B
2
−
2
B
B
′
+
B
′
2
≡
0
(
m
o
d
x
n
)
A
(
B
2
−
2
B
B
′
+
B
′
2
)
≡
0
(
m
o
d
x
n
)
因
为
A
×
B
≡
1
(
m
o
d
x
n
)
有
B
−
2
B
′
+
A
B
′
2
≡
0
(
m
o
d
x
n
)
B
−
2
B
′
+
A
B
′
2
≡
0
(
m
o
d
x
n
)
B
≡
2
B
′
−
A
B
′
2
(
m
o
d
x
n
)
B
≡
B
′
(
2
−
A
B
′
)
(
m
o
d
x
n
)
有A\times B \equiv 1(mod\ x^n)\\ 假设已知 B'\\ A\times B' \equiv 1(mod\ x^\frac{n}{2})\\ B-B' \equiv 0(mod\ x^\frac{n}{2})\\ (B-B')^2 \equiv 0(mod\ x^n)\\ B^2-2BB'+B'^2\equiv 0(mod\ x^n)\\ A(B^2-2BB'+B'^2)\equiv 0(mod\ x^n)\\ 因为A\times B \equiv 1(mod\ x^n)\\ 有B-2B'+AB'^2\equiv 0(mod\ x^n)\\ B-2B'+AB'^2\equiv 0(mod\ x^n)\\ B\equiv 2B'-AB'^2(mod\ x^n)\\ B\equiv B'(2-AB')(mod\ x^n)
有A×B≡1(mod xn)假设已知B′A×B′≡1(mod x2n)B−B′≡0(mod x2n)(B−B′)2≡0(mod xn)B2−2BB′+B′2≡0(mod xn)A(B2−2BB′+B′2)≡0(mod xn)因为A×B≡1(mod xn)有B−2B′+AB′2≡0(mod xn)B−2B′+AB′2≡0(mod xn)B≡2B′−AB′2(mod xn)B≡B′(2−AB′)(mod xn)
建议背诵全文
多项式ln
B
≡
l
n
A
(
m
o
d
x
n
)
B\equiv lnA(mod\ x^n)
B≡lnA(mod xn)
知 A 求 B
B
≡
l
n
A
(
m
o
d
x
n
)
同
时
求
导
有
B
′
≡
(
l
n
A
)
′
(
m
o
d
x
n
)
(
l
n
A
)
′
为
复
合
函
数
求
导
,
链
式
法
则
展
开
有
B
′
≡
l
n
′
A
×
A
′
(
m
o
d
x
n
)
B
′
≡
A
′
A
(
m
o
d
x
n
)
B\equiv lnA(mod\ x^n)\\ 同时求导有\\ B'\equiv (lnA)'(mod\ x^n)\\ (lnA)'为复合函数求导,链式法则展开有\\ B'\equiv ln'A\times A'(mod\ x^n)\\ B'\equiv \frac{A'}{A}(mod\ x^n)\\
B≡lnA(mod xn)同时求导有B′≡(lnA)′(mod xn)(lnA)′为复合函数求导,链式法则展开有B′≡ln′A×A′(mod xn)B′≡AA′(mod xn)
然后求导,求逆,卷积,积分即可
求
导
:
(
a
i
x
i
)
′
=
i
a
i
x
i
−
1
积
分
:
∫
a
i
x
i
d
x
=
a
i
1
i
+
1
x
i
+
1
求导:(a_i x^i)'=i a_i x^{i-1}\\ 积分:\int a_i x^i dx=a_i \frac{1}{i+1} x^{i+1}
求导:(aixi)′=iaixi−1积分:∫aixidx=aii+11xi+1
多项式牛顿迭代
直接上结论……
其实我也不会推
若
G
(
F
(
x
)
)
≡
0
(
m
o
d
≥
x
n
)
已
知
G
(
F
0
(
x
)
)
≡
0
(
m
o
d
n
2
)
则
F
(
x
)
≡
F
0
(
x
)
−
G
(
F
0
(
x
)
)
G
′
(
F
0
(
x
)
)
(
m
o
d
x
n
)
若G(F(x))\equiv 0(mod\ \geq x^n)\\ 已知G(F_0(x))\equiv 0(mod\ \frac{n}{2})\\ 则F(x)\equiv F_0(x)-\frac{G(F_0(x))}{G'(F_0(x))}(mod\ x^n)
若G(F(x))≡0(mod ≥xn)已知G(F0(x))≡0(mod 2n)则F(x)≡F0(x)−G′(F0(x))G(F0(x))(mod xn)
套路的将
A
A
A 化成常数,造出关于
B
B
B 的函数,然后带公式
于是很多问题可以倍增解决
多项式exp
e
A
(
x
)
≡
B
(
x
)
(
m
o
d
x
n
)
e^{A(x)}\equiv B(x)(mod\ x^n)
eA(x)≡B(x)(mod xn)
知 A 求 B
e
A
(
x
)
≡
B
(
x
)
(
m
o
d
x
n
)
设
G
(
B
(
x
)
)
≡
l
n
B
(
x
)
−
A
(
x
)
(
m
o
d
x
n
)
有
G
(
B
(
x
)
)
≡
0
(
m
o
d
x
n
)
所
以
B
(
x
)
≡
B
0
(
x
)
−
G
(
B
0
(
x
)
)
G
′
(
B
0
(
x
)
)
(
m
o
d
x
n
)
求
导
有
G
′
(
B
(
x
)
)
≡
B
(
x
)
−
1
(
m
o
d
x
n
)
代
入
有
B
(
x
)
≡
B
0
(
x
)
−
l
n
B
0
(
x
)
−
A
(
x
)
B
0
(
x
)
−
1
(
m
o
d
x
n
)
B
(
x
)
≡
B
0
(
x
)
−
(
l
n
B
0
(
x
)
−
A
(
x
)
)
B
0
(
x
)
(
m
o
d
x
n
)
B
(
x
)
≡
(
1
−
l
n
B
0
(
x
)
+
A
(
x
)
)
B
0
(
x
)
(
m
o
d
x
n
)
只
有
常
数
项
时
B
0
=
e
0
=
1
e^{A(x)}\equiv B(x)(mod\ x^n)\\ 设G(B(x))\equiv lnB(x)-A(x)(mod\ x^n)\\ 有G(B(x))\equiv 0(mod\ x^n)\\ 所以B(x)\equiv B_0(x)-\frac{G(B_0(x))}{G'(B_0(x))}(mod\ x^n)\\ 求导有G'(B(x))\equiv B(x)^{-1}(mod\ x^n)\\ 代入有B(x)\equiv B_0(x)-\frac{lnB_0(x)-A(x)}{B_0(x)^{-1}}(mod\ x^n)\\ B(x)\equiv B_0(x)-(lnB_0(x)-A(x))B_0(x)(mod\ x^n)\\ B(x)\equiv (1-lnB_0(x)+A(x))B_0(x)(mod\ x^n)\\ 只有常数项时B_0=e^0=1
eA(x)≡B(x)(mod xn)设G(B(x))≡lnB(x)−A(x)(mod xn)有G(B(x))≡0(mod xn)所以B(x)≡B0(x)−G′(B0(x))G(B0(x))(mod xn)求导有G′(B(x))≡B(x)−1(mod xn)代入有B(x)≡B0(x)−B0(x)−1lnB0(x)−A(x)(mod xn)B(x)≡B0(x)−(lnB0(x)−A(x))B0(x)(mod xn)B(x)≡(1−lnB0(x)+A(x))B0(x)(mod xn)只有常数项时B0=e0=1
倍增即可
多项式快速幂
B
(
x
)
≡
A
k
(
x
)
(
m
o
d
x
n
)
B(x)\equiv A^k(x)(mod\ x^n)
B(x)≡Ak(x)(mod xn)
知 A 求 B
公式十分简单
A
k
(
x
)
=
e
l
n
(
A
(
x
)
)
×
k
A^k(x)=e^{ln(A(x))\times k}
Ak(x)=eln(A(x))×k
于是一个求 ln,卷一个常数,再 exp 即可
对于常数项不为 1 的情况,如果有零,先左移 num 位到没有零,结果再右移 num*k 位零,然后可以整体除以常数项
a
0
a_0
a0,最后再乘
a
0
k
a_0^k
a0k
有零的模板:
#include <bits/stdc++.h>
using namespace std;
#define in Read()
#define int long long
inline char ch() {
static char buf[1 << 21], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2)
? EOF
: *p1++;
}
inline int in {
int s = 0, f = 1;
char x;
for (x = ch(); x < '0' || x > '9'; x = ch())
if (x == '-') f = -1;
for (; x >= '0' && x <= '9'; x = ch()) s = (s * 10) + (x & 15);
return f == 1 ? s : -s;
}
const int A = 1e6 + 5;
const int mod = 998244353;
const int phi = mod - 1;
int r[A], lim;
inline int power(int s, int c) {
int ans = 1;
while (c) {
if (c & 1) ans = ans * s % mod;
s = s * s % mod;
c >>= 1;
}
return ans;
}
inline void init(int len) {
lim = 1;
while (lim < len) lim <<= 1;
for (int i = 1; i < lim; i++)
r[i] = (r[i >> 1] >> 1) | ((i & 1) ? lim >> 1 : 0);
return;
}
inline void ntt(int *Q, int pos) {
for (int i = 0; i < lim; i++)
if (i < r[i]) swap(Q[i], Q[r[i]]);
for (int mid = 1; mid < lim; mid <<= 1) {
int wn = power(3, (mod - 1) / (mid << 1));
for (int i = 0; i < lim; i += (mid << 1)) {
int fw = 1;
for (int j = 0; j < mid; j++, fw = fw * wn % mod) {
int x = Q[i + j], y = Q[i + j + mid] * fw % mod;
Q[i + j] = (x + y) % mod;
Q[i + j + mid] = (x - y + mod) % mod;
}
}
}
if (pos) return;
int k = power(lim, mod - 2);
reverse(Q + 1, Q + lim);
for (int i = 0; i < lim; i++) Q[i] = Q[i] * k % mod;
return;
}
inline void Der(int *x, int *y, int len) {
for (int i = 0; i < len; i++) y[i] = x[i + 1] * (i + 1) % mod;
return;
}
int inv_ing[A];
inline void Ing(int *x, int *y, int len) {
#define inv inv_ing
inv[1] = 1;
for (int i = 2; i < len; i++)
inv[i] = (-(mod / i) * inv[mod % i] % mod + mod) % mod;
for (int i = 1; i < len; i++) y[i] = x[i - 1] * inv[i] % mod;
y[0] = 0;
#undef inv
return;
}
inline void Flow(int *x, int *y, int lx, int ly) {
init(lx + ly);
ntt(x, 1), ntt(y, 1);
for (int i = 0; i < lim; i++) x[i] = x[i] * y[i] % mod;
ntt(x, 0);
return;
}
int tmp_inv[A];
inline void Inv(int *x, int *y, int len) {
#define tmp tmp_inv
if (len == 1) {
y[0] = power(x[0], mod - 2);
return;
}
Inv(x, y, (len + 1) >> 1);
init(len << 1);
for (int i = 0; i < len; i++) tmp[i] = x[i];
for (int i = len; i < lim; i++) tmp[i] = 0;
ntt(y, 1), ntt(tmp, 1);
for (int i = 0; i < lim; i++)
y[i] = (2 - y[i] * tmp[i] % mod + mod) % mod * y[i] % mod;
ntt(y, 0);
for (int i = len; i < lim; i++) y[i] = 0;
for (int i = 0; i < lim; i++) tmp[i] = 0;
#undef tmp
}
int p1_ln[A], p2_ln[A];
inline void Ln(int *x, int *y, int len) {
#define p1 p1_ln
#define p2 p2_ln
Der(x, p1, len);
Inv(x, p2, len);
Flow(p1, p2, len, len);
Ing(p1, y, len);
for (int i = len; i < lim; i++) y[i] = 0;
for (int i = 0; i < lim; i++) p1[i] = p2[i] = 0;
return;
#undef p1
#undef p2
}
int tmp_exp[A];
inline void Exp(int *x, int *y, int len) {
#define tmp tmp_exp
if (len == 1) {
y[0] = 1;
return;
}
Exp(x, y, (len + 1) >> 1);
Ln(y, tmp, len);
init(len << 1);
for (int i = 0; i < len; i++) tmp[i] = (x[i] - tmp[i] + mod) % mod;
tmp[0] = (tmp[0] + 1) % mod;
for (int i = len; i < lim; i++) tmp[i] = 0;
Flow(y, tmp, len, len);
for (int i = len; i < lim; i++) y[i] = 0;
for (int i = 0; i < lim; i++) tmp[i] = 0;
#undef tmp
return;
}
int tmp_power[A];
inline void Power(int *x, int *y, int len, int c) {
#define tmp tmp_power
Ln(x, tmp, len);
for (int i = 0; i < len; i++) tmp[i] = tmp[i] * c % mod;
Exp(tmp, y, len);
#undef tmp
return;
}
int n, K, PK, maxx;
int a[A], b[A];
int num, mul;
inline void prepare() {
for (num = 0; num < n; num++)
if (a[num]) break;
for (int i = num; i < n; i++) a[i - num] = a[i];
mul = a[0];
int k = power(a[0], mod - 2);
for (int i = 0; i < n; i++) a[i] = a[i] * k % mod;
return;
}
inline void print() {
if (num * maxx >= n) {
for (int i = 0; i < n; i++) printf("0 ");
return;
}
for (int i = 0; i < num * maxx; i++) printf("0 ");
int k = power(mul, PK);
for (int i = 0; i < n - num; i++) b[i] = b[i] * k % mod;
for (int i = 0; i < n - num * maxx; i++) printf("%lld ", b[i]);
return;
}
signed main() {
n = in;
char x;
while (x < '0' || x > '9') x = ch();
while (x >= '0' && x <= '9') {
K = ((K * 10) % mod + (x & 15)) % mod;
PK = ((PK * 10) % phi + (x & 15)) % phi;
if ((maxx * 10) + (x & 15) < mod) maxx = (maxx * 10) + (x & 15);
x = ch();
}
for (int i = 0; i < n; i++) a[i] = in;
prepare();
if (num * maxx >= n) {
print();
return 0;
}
Power(a, b, n - num, K);
print();
return 0;
}
多项式开根
A
(
x
)
≡
B
(
x
)
(
m
o
d
x
n
)
\sqrt{A(x)}\equiv B(x)(mod\ x^n)
A(x)≡B(x)(mod xn)
知 A 求 B
有
B
(
x
)
2
−
A
(
x
)
≡
0
(
m
o
d
x
n
)
设
G
(
B
(
x
)
)
≡
B
(
x
)
2
−
A
(
x
)
(
m
o
d
x
n
)
有
B
(
x
)
≡
B
0
(
x
)
−
G
(
B
0
(
x
)
)
G
′
(
B
0
(
x
)
)
(
m
o
d
x
n
)
因
为
G
′
(
B
(
x
)
)
≡
2
B
(
x
)
(
m
o
d
x
n
)
所
以
B
(
x
)
≡
B
0
(
x
)
−
B
0
2
(
x
)
−
A
(
x
)
2
B
0
(
x
)
(
m
o
d
x
n
)
B
(
x
)
≡
A
(
x
)
+
B
0
2
(
x
)
2
B
0
(
x
)
(
m
o
d
x
n
)
B
(
x
)
≡
1
2
(
A
(
x
)
B
0
(
x
)
+
B
0
(
x
)
)
(
m
o
d
x
n
)
有B(x)^2-A(x)\equiv 0(mod\ x^n)\\ 设G(B(x))\equiv B(x)^2-A(x)(mod\ x^n)\\ 有B(x)\equiv B_0(x)-\frac {G(B_0(x))}{G'(B_0(x))}(mod\ x^n)\\ 因为G'(B(x))\equiv 2B(x)(mod\ x^n)\\ 所以B(x)\equiv B_0(x)-\frac{B_0^2(x)-A(x)}{2B_0(x)}(mod\ x^n)\\ B(x)\equiv \frac{A(x)+B_0^2(x)}{2B_0(x)}(mod\ x^n)\\ B(x)\equiv \frac{1}{2} (\frac{A(x)}{B_0(x)}+B_0(x))(mod\ x^n)
有B(x)2−A(x)≡0(mod xn)设G(B(x))≡B(x)2−A(x)(mod xn)有B(x)≡B0(x)−G′(B0(x))G(B0(x))(mod xn)因为G′(B(x))≡2B(x)(mod xn)所以B(x)≡B0(x)−2B0(x)B02(x)−A(x)(mod xn)B(x)≡2B0(x)A(x)+B02(x)(mod xn)B(x)≡21(B0(x)A(x)+B0(x))(mod xn)
倍增即可
当多项式只有常数项时,开根的结果是二次剩余,题目保证
a
0
=
1
a_0=1
a0=1,那么直接赋为 1 即可
多项式除法
F
(
x
)
=
Q
(
x
)
∗
G
(
x
)
+
R
(
x
)
F(x)=Q(x)*G(x)+R(x)
F(x)=Q(x)∗G(x)+R(x)
知
F
和
G
F和G
F和G 求
Q
和
R
Q和R
Q和R
将
1
x
代
入
有
F
(
1
x
)
=
Q
(
1
x
)
∗
G
(
1
x
)
+
R
(
1
x
)
发
现
x
n
F
(
1
x
)
与
F
(
x
)
的
系
数
颠
倒
令
F
R
(
x
)
=
x
n
F
(
x
)
于
是
x
n
F
(
1
x
)
=
x
n
Q
(
1
x
)
∗
G
(
1
x
)
+
x
n
R
(
1
x
)
x
n
F
(
1
x
)
=
x
n
−
m
Q
(
1
x
)
∗
x
m
G
(
1
x
)
+
x
n
−
m
+
1
x
m
−
1
R
(
1
x
)
F
R
(
x
)
=
Q
R
(
x
)
∗
G
R
(
x
)
+
x
n
−
m
+
1
R
R
(
x
)
F
R
(
x
)
=
Q
R
(
x
)
∗
G
R
(
x
)
(
m
o
d
x
n
−
m
+
1
)
Q
R
(
x
)
=
F
R
(
x
)
∗
G
R
−
1
(
x
)
(
m
o
d
x
n
−
m
+
1
)
将 \frac{1}{x} 代入\\ 有F(\frac{1}{x})=Q(\frac{1}{x})*G(\frac{1}{x})+R(\frac{1}{x})\\ 发现x^n F(\frac{1}{x})与F(x)的系数颠倒\\ 令F_R(x)=x^n F(x)\\ 于是x^n F(\frac{1}{x})=x^n Q(\frac{1}{x})*G(\frac{1}{x})+x^n R(\frac{1}{x})\\ x^n F(\frac{1}{x})=x^{n-m} Q(\frac{1}{x})*x^m G(\frac{1}{x})+x^{n-m+1}x^{m-1} R(\frac{1}{x})\\ F_R(x)=Q_R(x)*G_R(x)+x^{n-m+1}R_R(x)\\ F_R(x)=Q_R(x)*G_R(x)(mod\ x^{n-m+1})\\ Q_R(x)=F_R(x)*G_R^{-1}(x)(mod\ x^{n-m+1})
将x1代入有F(x1)=Q(x1)∗G(x1)+R(x1)发现xnF(x1)与F(x)的系数颠倒令FR(x)=xnF(x)于是xnF(x1)=xnQ(x1)∗G(x1)+xnR(x1)xnF(x1)=xn−mQ(x1)∗xmG(x1)+xn−m+1xm−1R(x1)FR(x)=QR(x)∗GR(x)+xn−m+1RR(x)FR(x)=QR(x)∗GR(x)(mod xn−m+1)QR(x)=FR(x)∗GR−1(x)(mod xn−m+1)
反转系数后对 G 求逆,卷上 F 再反转系数就能求出 Q
然后
R
(
x
)
=
F
(
x
)
−
Q
(
x
)
∗
G
(
x
)
R(x)=F(x)-Q(x)*G(x)
R(x)=F(x)−Q(x)∗G(x) 即可
多项式多点求值
给出多项式 F(x),求其在
x
1
,
x
2
…
…
x
n
x_1,x_2……x_n
x1,x2……xn 处的值
即求
f
(
x
1
)
,
f
(
x
2
)
…
…
f
(
x
n
)
f(x_1),f(x_2)……f(x_n)
f(x1),f(x2)……f(xn)
考虑构造 G(x) 和 H(x)
g
(
x
)
=
∏
i
=
1
n
2
(
x
−
x
i
)
h
(
x
)
=
∏
i
=
n
2
+
1
n
(
x
−
x
i
)
g(x)=\prod_{i=1}^{\frac{n}{2}} (x-x_i)\\ h(x)=\prod_{i=\frac{n}{2} +1}^{n} (x-x_i)
g(x)=i=1∏2n(x−xi)h(x)=i=2n+1∏n(x−xi)
有
f
(
x
i
)
=
(
F
%
G
)
(
x
i
)
(
i
≤
n
2
)
f
(
x
i
)
=
(
F
%
H
)
(
x
i
)
(
i
>
n
2
)
f(x_i)=(F\% G)(x_i)(i\leq \frac{n}{2})\\ f(x_i)=(F\% H)(x_i)(i> \frac{n}{2})
f(xi)=(F%G)(xi)(i≤2n)f(xi)=(F%H)(xi)(i>2n)
对于两边分治递归处理
多项式全家桶
#include <bits/stdc++.h>
using namespace std;
#define re register
#define int long long
namespace IO {
char _buf[1 << 21], *_p1 = _buf, *_p2 = _buf;
#define ch() \
(_p1 == _p2 && \
(_p2 = (_p1 = _buf) + fread(_buf, 1, 1 << 21, stdin), _p1 == _p2) \
? EOF \
: *_p1++)
inline int in() {
int s = 0, f = 1;
char x = ch();
while (x < '0' || x > '9') {
if (x == '-') f = -1;
x = ch();
}
while (x >= '0' && x <= '9') {
s = (s * 10) + (x & 15);
x = ch();
}
return f = 1 ? s : -s;
}
char _buf_[1 << 21];
int _p1_ = -1;
inline void flush() {
fwrite(_buf_, 1, _p1_ + 1, stdout);
_p1_ = -1;
}
inline void pc(char x) {
if (_p1_ == (1 << 21) - 1) flush();
_buf_[++_p1_] = x;
}
inline void out(int x) {
char k[20];
int tot = 0;
if (!x) {
pc('0');
return;
}
if (x < 0) {
pc('-');
x = -x;
}
while (x) {
k[++tot] = (x % 10) | 48;
x /= 10;
}
for (int i = tot; i; i--) pc(k[i]);
return;
}
} // namespace IO
using namespace IO;
typedef vector<int> poly;
const int A = 3e5 + 5;
const int logA = 18;
const int mod = 998244353;
inline int add(int a, int b) { return a + b >= mod ? a + b - mod : a + b; }
inline int dec(int a, int b) { return a - b < 0 ? a - b + mod : a - b; }
inline int mul(int a, int b) { return a * b % mod; }
inline void Add(int &a, int b) {
a = add(a, b);
return;
}
inline void Dec(int &a, int b) {
a = dec(a, b);
return;
}
inline void Mul(int &a, int b) {
a = mul(a, b);
return;
}
inline int power(int s, int c) {
int ans = 1;
while (c) {
if (c & 1) Mul(ans, s);
s = mul(s, s);
c >>= 1;
}
return ans;
}
inline void print(poly a) {
for (int i = 0; i < a.size(); i++) out(a[i]), pc(' ');
pc('\n');
return;
}
int _r[A], _lim;
inline void init(int len) {
_lim = 1;
while (_lim < len) _lim <<= 1;
for (int i = 1; i < _lim; i++)
_r[i] = (_r[i >> 1] >> 1) | ((i & 1) ? _lim >> 1 : 0);
return;
}
inline poly operator+(poly a, poly b) {
if (a.size() < b.size()) a.resize(b.size());
for (int i = 0; i < b.size(); i++) Add(a[i], b[i]);
return a;
}
inline poly operator-(poly a, poly b) {
if (a.size() < b.size()) a.resize(b.size());
for (int i = 0; i < b.size(); i++) Dec(a[i], b[i]);
return a;
}
inline poly Divx(poly a) {
for (int i = 0; i < a.size(); i++) a[i] = a[i + 1];
a.pop_back();
return a;
}
int *_wn[logA + 1], inv[A], fact[A];
inline void pre_ntt() {
for (int i = 1; i <= logA; i++) _wn[i] = new int[1 << (i - 1)];
int k = power(3, (mod - 1) / (1 << logA));
_wn[logA][0] = 1;
for (int i = 1; i < (1 << (logA - 1)); i++)
_wn[logA][i] = mul(_wn[logA][i - 1], k);
for (int i = logA - 1; i; i--)
for (int j = 0; j < (1 << (i - 1)); j++) _wn[i][j] = _wn[i + 1][j << 1];
inv[0] = inv[1] = 1;
for (int i = 2; i <= (1 << logA); i++)
inv[i] = mul(mod - mod / i, inv[mod % i]);
fact[0] = 1;
for (int i = 1; i <= (1 << logA); i++) fact[i] = mul(fact[i - 1], i);
return;
}
inline void ntt(poly &Q, int pos) {
for (int i = 0; i < _lim; i++)
if (i < _r[i]) swap(Q[_r[i]], Q[i]);
for (int mid = 1, c = 1; mid < _lim; mid <<= 1, c++)
for (int i = 0; i < _lim; i += (mid << 1))
for (int j = 0; j < mid; j++) {
int x = Q[i + j], y = mul(Q[i + j + mid], _wn[c][j]);
Q[i + j] = add(x, y);
Q[i + j + mid] = dec(x, y);
}
if (pos) return;
int k = power(_lim, mod - 2);
reverse(Q.begin() + 1, Q.end());
for (int i = 0; i < _lim; i++) Mul(Q[i], k);
return;
}
inline poly operator*(poly a, poly b) {
int len = a.size() + b.size() - 1;
if (a.size() < 32 || b.size() < 32) {
poly c(len, 0);
for (int i = 0; i < a.size(); i++)
for (int j = 0; j < b.size(); j++) Add(c[i + j], mul(a[i], b[j]));
return c;
}
init(len);
a.resize(_lim), b.resize(_lim);
ntt(a, 1), ntt(b, 1);
for (int i = 0; i < _lim; i++) Mul(a[i], b[i]);
ntt(a, 0);
a.resize(len);
return a;
}
inline poly Inv(poly a, int len) {
poly b(1, power(a[0], mod - 2)), c;
for (int deg = 2; (deg >> 1) < len; deg <<= 1) {
c.resize(deg);
init(deg << 1);
for (int i = 0; i < deg; i++) c[i] = i < a.size() ? a[i] : 0;
c.resize(_lim), b.resize(_lim);
ntt(c, 1), ntt(b, 1);
for (int i = 0; i < _lim; i++) Mul(b[i], dec(2, mul(b[i], c[i])));
ntt(b, 0);
b.resize(deg);
}
b.resize(len);
return b;
}
inline poly Der(poly a) {
for (int i = 0; i < a.size(); i++) a[i] = mul(a[i + 1], i + 1);
a.pop_back();
return a;
}
inline poly Ing(poly a) {
a.push_back(0);
for (int i = a.size() - 1; i; i--) a[i] = mul(inv[i], a[i - 1]);
a[0] = 0;
return a;
}
inline poly Ln(poly a, int len) {
a = Ing(Der(a) * Inv(a, len));
a.resize(len);
return a;
}
inline poly Exp(poly a, int len) {
poly b(1, 1), c;
for (int deg = 2; deg < (len << 1); deg <<= 1) {
c = Ln(b, deg);
Dec(c[0], 1);
for (int i = 0; i < deg; i++) c[i] = dec(i < a.size() ? a[i] : 0, c[i]);
b = b * c;
b.resize(deg);
}
b.resize(len);
return b;
}
inline poly Power(poly a, int c, int len) {
a = Ln(a, len);
for (int i = 0; i < a.size(); i++) Mul(a[i], c);
return Exp(a, len);
}
inline poly Sqrt(poly a, int len) {
poly b(1, 1), c;
for (int deg = 2; deg < (len << 1); deg <<= 1) {
b = a * Inv(b, deg) + b;
for (int i = 0; i < deg; i++) b[i] = mul(b[i], inv[2]);
b.resize(deg);
}
b.resize(len);
return b;
}
inline void operator-=(poly &a, const poly &b) {
if (a.size() < b.size()) a.resize(b.size());
for (int i = 0; i < b.size(); i++) Dec(a[i], b[i]);
return;
}
inline poly operator/(poly a, poly b) {
int len = a.size() - b.size() + 1;
reverse(a.begin(), a.end());
a.resize(len);
reverse(b.begin(), b.end());
a = a * Inv(b, len);
a.resize(len);
reverse(a.begin(), a.end());
return a;
}
inline poly operator%(poly a, poly b) {
if (a.size() < b.size()) return a;
a -= b * (a / b);
a.resize(b.size() - 1);
return a;
}
poly _f[A];
#define lch p << 1
#define rch p << 1 | 1
inline void pre_val(int p, int l, int r, int *x) {
if (l == r) {
_f[p].push_back(dec(0, x[l]));
_f[p].push_back(1);
return;
}
int mid = (l + r) >> 1;
pre_val(lch, l, mid, x), pre_val(rch, mid + 1, r, x);
_f[p] = _f[lch] * _f[rch];
return;
}
inline void Val(int p, int l, int r, poly F, int *v) {
if (l == r) {
v[l] = F[0];
return;
}
int mid = (l + r) >> 1;
Val(lch, l, mid, F % _f[lch], v), Val(rch, mid + 1, r, F % _f[rch], v);
return;
}
#undef lch
#undef rch