yaoxi-std 的博客

$\text{开}\mathop{\text{卷}}\limits^{ju\check{a}n}\text{有益}$

0%

【专题】多项式

【专题】多项式

主要为$FFT$,$NTT$,多项式求逆等变换,可以和生成函数结合起来考计数题。这篇题解中有对多项式乘法的较详细证明过程。

对于使用生成函数判断方案是否合法(而不是统计方案的数量),使用单模数容易被卡,建议在$998244353$,$1004535809$和$469762049$中选取两个,其原根都为$3$。

若题目中要求对奇怪的数取模,一定记得计算模数的原根,有可能不是$3$!

题目

P3803 【模版】多项式乘法(FFT)
P4245 【模版】任意模数多项式乘法
P4721 【模版】分治FFT 题解
P4238 【模版】多项式求逆 题解
CF632E Thief in a Shop 题解
CF986D Perfect Encoding
CF1613F Tree Coloring
CF755G PolandBall and Many Other Balls
CF623E Transforming Sequence
CF773F Test Data Generation
CF553E Kyoya and Train
P4725 【模版】多项式对数函数(多项式ln)
P4726 【模版】多项式指数函数(多项式exp)
P4002 生成树计数
P5850 calc
P4491 [HAOI] 染色

模版代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
const long double MPI = acos(-1);
const int MOD = 998244353;
// const int MOD = 1004535809; // 备用
// const int MOD = 469762049; // 备用
namespace polynomial {
int add(int x, int y) {
x += y;
return x >= MOD ? x - MOD : x;
}
int sub(int x, int y) {
x -= y;
return x < 0 ? x + MOD : x;
}
int qpow(int x, int y, int p = MOD) {
int ret = 1;
for (; y; y >>= 1, x = x * x % p)
if (y & 1)
ret = ret * x % p;
return ret;
}
template <class _Tp>
void change(_Tp* f, int len) {
static int rev[MAXN] = {};
for (int i = 0; i < len; ++i) {
rev[i] = rev[i >> 1] >> 1;
if (i & 1)
rev[i] |= len >> 1;
}
for (int i = 0; i < len; ++i)
if (i < rev[i])
swap(f[i], f[rev[i]]);
}
void ntt(int* f, int len, int on) {
change(f, len);
for (int h = 2; h <= len; h <<= 1) {
int gn = qpow(3, (MOD - 1) / h);
for (int j = 0; j < len; j += h) {
int g = 1;
for (int k = j; k < j + h / 2; ++k) {
int u = f[k], t = g * f[k + h / 2] % MOD;
f[k] = add(u, t), f[k + h / 2] = sub(u, t);
g = g * gn % MOD;
}
}
}
if (on == -1) {
reverse(f + 1, f + len);
int inv = qpow(len, MOD - 2);
for (int i = 0; i < len; ++i)
f[i] = f[i] * inv % MOD;
}
}
int polymul(const int* f, int n, const int* g, int m, int* ans) {
static int tf[MAXN] = {}, tg[MAXN] = {};
int len = 1;
while (len < n + m - 1)
len <<= 1;
copy(f, f + n, tf);
fill(tf + n, tf + len, 0);
copy(g, g + m, tg);
fill(tg + m, tg + len, 0);
ntt(tf, len, 1);
ntt(tg, len, 1);
for (int i = 0; i < len; ++i)
tf[i] = tf[i] * tg[i] % MOD;
ntt(tf, len, -1);
copy(tf, tf + n + m - 1, ans);
return n + m - 1;
}
int polyinv(const int* f, int n, int* ans) {
static int tmp[MAXN] = {};
int len = 1;
while (len < n)
len <<= 1;
fill(ans, ans + len + len, 0);
ans[0] = qpow(f[0], MOD - 2);
for (int h = 2; h <= len; h <<= 1) {
copy(f, f + h, tmp);
fill(tmp + h, tmp + h + h, 0);
ntt(tmp, h + h, 1);
ntt(ans, h + h, 1);
for (int i = 0; i < h + h; ++i)
ans[i] = ans[i] * (2 - ans[i] * tmp[i] % MOD + MOD) % MOD;
ntt(ans, h + h, -1);
fill(ans + h, ans + h + h, 0);
}
return n;
}
int derivation(const int* f, int n, int* ans) {
for (int i = 0; i < n - 1; ++i)
ans[i] = f[i + 1] * (i + 1) % MOD;
return ans[n - 1] = 0, n - 1;
}
int integral(const int* f, int n, int* ans) {
for (int i = n; i >= 1; --i)
ans[i] = f[i - 1] * qpow(i, MOD - 2) % MOD;
return ans[0] = 0, n + 1;
}
int ln(const int* f, int n, int* ans) {
static int tf[MAXN] = {}, tg[MAXN] = {};
derivation(f, n, tf);
polyinv(f, n, tg);
polymul(tf, n - 1, tg, n, ans);
integral(ans, n - 1, ans);
fill(ans + n, ans + n + n, 0);
return n;
}
int exp(const int* f, int n, int* ans) {
static int tmp[MAXN] = {};
ans[0] = 1, ans[1] = 0;
for (int h = 2; h <= (n << 1); h <<= 1) {
ln(ans, h, tmp);
for (int i = 0; i < h; ++i)
tmp[i] = add(i == 0, sub(f[i], tmp[i]));
polymul(ans, h, tmp, h, ans);
}
return n;
}
}; // namespace polynomial