多项式

多项式

多项式操作

模板

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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#define Poly vector<int>
#define len(A) ((int) A.size())
namespace Pol {
inline int add(int a, int b) { return (a += b) >= p ? a -= p : a; }
inline int mul(int a, int b) { return 1ll * a * b % p; }
Poly operator - (const int &v, const Poly &a) {
Poly res(a);
for (int i = 0; i < len(res); ++i) res[i] = p - res[i];
res[0] = add(res[0], v); return res;
}
Poly operator - (const Poly &a, const int &v) {
Poly res(a); res[0] = add(res[0], p - v); return res;
}
Poly operator * (const Poly &a, const int &v) {
Poly res(a);
for (int i = 0; i < len(res) ; ++i) res[i] = mul(res[i], v);
return res;
}

const int N = 4200000;
const int G = 3;

int P[N], inv[N], fac[N];
void init_P(int n) {
int l = 0; while ((1 << l) < n) ++l;
for (int i = 0; i < n; ++i) P[i] = (P[i >> 1] >> 1) | ((i & 1) << l - 1);
}
void init_C() {
if (fac[0]) return ;
fac[0] = 1; for (int i = 1; i < N; ++i) fac[i] = mul(fac[i - 1], i);
inv[N - 1] = pow_mod(fac[N - 1], p - 2); for (int i = N - 2; ~i; --i) inv[i] = mul(inv[i + 1], i + 1);
}
vector<int> init_W(int n) {
vector<int> w(n); w[1] = 1;
for (int i = 2; i < n; i <<= 1) {
auto w0 = w.begin() + i / 2, w1 = w.begin() + i;
int wn = pow_mod(G, (p - 1) / (i << 1));
for (int j = 0; j < i; j += 2)
w1[j] = w0[j >> 1], w1[j + 1] = mul(w1[j], wn);
}
return w;
} auto w = init_W(1 << 21);
void DIT(Poly &a) {
int n = len(a);
for (int k = n >> 1; k; k >>= 1)
for (int i = 0; i < n; i += k << 1)
for (int j = 0; j < k; ++j) {
int x = a[i + j], y = a[i + j + k];
a[i + j + k] = mul(add(x, p - y), w[k + j]), a[i + j] = add(x, y);
}
}
void DIF(Poly &a) {
int n = len(a);
for (int k = 1; k < n; k <<= 1)
for (int i = 0; i < n; i += k << 1)
for (int j = 0; j < k; ++j) {
int x = a[i + j], y = mul(a[i + j + k], w[k + j]);
a[i + j + k] = add(x, p - y), a[i + j] = add(x, y);
}
int inv = pow_mod(n, p - 2);
for (int i = 0; i < n; ++i) a[i] = mul(a[i], inv);
reverse(a.begin() + 1, a.end());
}
Poly operator * (const Poly &A, const Poly &B) {
int n = 1, n1 = len(A), n2 = len(B); while (n < n1 + n2 - 1) n <<= 1; init_P(n);
Poly a(n), b(n);
for (int i = 0; i < n1; ++i) a[i] = add(A[i], p);
for (int i = 0; i < n2; ++i) b[i] = add(B[i], p);
DIT(a); DIT(b);
for (int i = 0; i < n; ++i) a[i] = mul(a[i], b[i]);
DIF(a); a.resize(n1 + n2 - 1); return a;
}
Poly MMul(const Poly &A, const Poly &B) { // 差卷积, 默认 A 和 B 的长度相同
int n = 1, L = len(A); while (n < 2 * L - 1) n <<= 1; init_P(n);
Poly a(n), b(n);
for (int i = 0; i < L; ++i) a[i] = add(A[i], p);
for (int i = 0; i < L; ++i) b[i] = add(B[i], p);
reverse(b.begin(), b.begin() + L);
DIT(a); DIT(b);
for (int i = 0; i < n; ++i) a[i] = mul(a[i], b[i]);
DIF(a); a.resize(L); reverse(a.begin(), a.end()); return a;
}
Poly Der(const Poly &a) {
Poly res(a);
for (int i = 0; i < len(a) - 1; ++i) res[i] = mul(i + 1, res[i + 1]);
res[len(a) - 1] = 0; return res;
}
Poly Int(const Poly &a) {
static int inv[N];
if (!inv[1]) {
inv[1] = 1;
for (int i = 2; i < N; ++i) inv[i] = mul(p - p / i, inv[p % i]);
}
Poly res(a); res.resize(len(a) + 1);
for (int i = len(a); i; --i) res[i] = mul(res[i - 1], inv[i]);
res[0] = 0; return res;
}
Poly Inv(const Poly &a) {
Poly res(1, pow_mod(a[0], p - 2));
int n = 1; while (n < len(a)) n <<= 1;
for (int k = 2; k <= n; k <<= 1) {
int L = 2 * k; init_P(L); Poly t(L);
copy_n(a.begin(), min(k, len(a)), t.begin());
t.resize(L); res.resize(L);
DIT(res); DIT(t);
for (int i = 0; i < L; ++i) res[i] = mul(res[i], add(2, p - mul(t[i], res[i])));
DIF(res); res.resize(k);
} res.resize(len(a)); return res;
}
Poly Offset(const Poly &a, int c) {
int n = len(a); init_C();
Poly t1(n), t2(n);
for (int i = 0; i < n; ++i) t1[i] = mul(pow_mod(c, i), inv[i]);
for (int i = 0; i < n; ++i) t2[i] = mul(a[i], fac[i]);
t1 = MMul(t1, t2);
for (int i = 0; i < n; ++i) t1[i] = mul(t1[i], inv[i]);
return t1;
}
pair<Poly, Poly> Divide(const Poly &a, const Poly &b) {
int n = len(a), m = len(b);
Poly t1(a.rbegin(), a.rbegin() + n - m + 1), t2(b.rbegin(), b.rend()); t2.resize(n - m + 1);
Poly Q = Inv(t2) * t1; Q.resize(n - m + 1); reverse(Q.begin(), Q.end());
Poly R = Q * b; R.resize(m - 1); for (int i = 0; i < len(R); ++i) R[i] = add(a[i], p - R[i]);
return make_pair(Q, R);
}
Poly Ln(const Poly &a) {
Poly res = Int(Der(a) * Inv(a));
res.resize(len(a)); return res;
}
Poly Exp(const Poly &a) {
Poly res(1, 1);
int n = 1; while (n < len(a)) n <<= 1;
for (int k = 2; k <= n; k <<= 1) {
Poly t(res.begin(), res.end()); t.resize(k); t = Ln(t);
for (int i = 0; i < min(len(a), k); ++i) t[i] = add(a[i], p - t[i]); t[0] = add(t[0], 1);
res = res * t; res.resize(k);
} res.resize(len(a)); return res;
}
Poly Sqrt(const Poly &a) { // a[0] = 1
Poly res(1, 1); ll inv2 = pow_mod(2, p - 2);
int n = 1; while (n < len(a)) n <<= 1;
for (int k = 2; k <= n; k <<= 1) {
Poly t(res.begin(), res.end()), ta(a.begin(), a.begin() + min(len(a), k));
t.resize(k); t = Inv(t) * ta;
res.resize(k); for (int i = 0; i < k; ++i) res[i] = mul(add(res[i], t[i]), inv2);
} res.resize(len(a)); return res;
}
Poly Pow(const Poly &a, int k) { // a[0] = 1
return Exp(Ln(a) * k);
}
Poly Pow(const Poly &a, int k, int kk) {
int n = len(a), t = n, m, v, inv, powv; Poly res(n);
for (int i = n - 1; ~i; --i) if (a[i]) t = i, v = a[i];
if (k && t >= (n + k - 1) / k) return res;
if (t == n) { if (!k) res[0] = 1; return res; }
m = n - t * k; res.resize(m);
inv = pow_mod(v, p - 2); powv = pow_mod(v, kk);
for (int i = 0; i < m; ++i) res[i] = mul(a[i + (k > 0) * t], inv);
res = Exp(Ln(res) * k); res.resize(n);
for (int i = m - 1; ~i; --i) {
int tmp = mul(res[i], powv);
res[i] = 0, res[i + t * k] = tmp;
}
return res;
}
} // namespace Pol

O(n^2) 模板

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
#define Poly vector<int>
#define len(a) (a.size())
namespace Pol {
Poly operator + (const Poly &u, const Poly &v) { // len(u) == len(v)
int n = len(u); Poly res(n);
for (int i = 0; i < n; ++i) res[i] = add(u[i], v[i]);
return res;
}
Poly operator - (const Poly &u, const Poly &v) { // len(u) == len(v)
int n = len(u); Poly res(n);
for (int i = 0; i < n; ++i) res[i] = add(u[i], p - v[i]);
return res;
}
/*Poly operator * (const Poly &u, const Poly &v) {
int n1 = len(u), n2 = len(v); Poly res(n1 + n2 - 1);
for (int i = 0; i < n1; ++i)
for (int j = 0; j < n2; ++j) res[i + j] = add(res[i + j], mul(u[i], v[j]));
return res;
}*/
Poly operator * (const Poly &u, const Poly &v) { // len(u) == len(v)
int n = len(u); Poly res(n);
for (int i = 0; i < n; ++i)
for (int j = 0; i + j < n; ++j) res[i + j] = add(res[i + j], mul(u[i], v[j]));
return res;
}
Poly Inv(const Poly &a) {
int n = len(a); Poly res(n); res[0] = pow_mod(a[0], p - 2); Poly ta(n);
for (int i = 1; i < n; ++i) ta[i] = mul(a[i], res[0]);
for (int i = 1; i < n; ++i)
for (int j = 1; j <= i; ++j) res[i] = add(res[i], mul(p - ta[j], res[i - j]));
return res;
}
}

任意模数模板

三模NTT

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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
const int mod1 = 998244353, mod2 = 1004535809, mod3 = 469762049, G = 3;
const ll mod12 = 1ll * mod1 * mod2;
const int inv1 = pow_mod(mod1, mod2 - 2, mod2), inv2 = pow_mod(mod12 % mod3, mod3 - 2, mod3);
inline int mod(int x, int p) { return x >= p ? x - p : x; }
struct Int {
int x, y, z;

Int() { x = y = z = 0; }
Int(int x, int y, int z) : x(x % mod1), y(y % mod2), z(z % mod3) {}
Int(int v) : x(v % mod1), y(v % mod2), z(v % mod3) {}

static inline int add(int x, int y, int p) { return (x += y) >= p ? x - p : x; }
inline friend Int operator + (const Int &u, const Int &v) {
return Int(add(u.x, v.x, mod1), add(u.y, v.y, mod2), add(u.z, v.z, mod3));
}
inline friend Int operator - (const Int &u, const Int &v) {
return Int(add(u.x, mod1 - v.x, mod1), add(u.y, mod2 - v.y, mod2), add(u.z, mod3 - v.z, mod3));
}
inline friend Int operator * (const Int &u, const Int &v) {
return Int(1ll * u.x * v.x % mod1, 1ll * u.y * v.y % mod2, 1ll * u.z * v.z % mod3);
}
inline int get() const {
ll v = 1ll * add(y, mod2 - x, mod2) * inv1 % mod2 * mod1 + x;
return (1ll * add(z, mod3 - v % mod3, mod3) * inv2 % mod3 * (mod12 % p) % p + v) % p;
}
};

typedef vector<Int> Poly;
#define len(a) ((int) a.size())
namespace Pol {
Poly operator - (const Int &v, const Poly &a) {
Poly res(a);
for (int i = 0; i < len(res); ++i) res[i] = p - res[i];
res[0] = res[0] + v; return res;
}
Poly operator - (const Poly &a, const Int &v) {
Poly res(a); res[0] = res[0] + (p - v); return res;
}
Poly operator * (const Poly &a, const Int &v) {
Poly res(a);
for (int i = 0; i < len(res) ; ++i) res[i] = res[i] * v;
return res;
}

const int N = 4200000;

int P[N];
void init_P(int n) {
int l = 0; while ((1 << l) < n) ++l;
for (int i = 0; i < n; ++i) P[i] = (P[i >> 1] >> 1) | ((i & 1) << l - 1);
}
vector<Int> init_W(int n) {
vector<Int> w(n); w[1] = 1;
for (int i = 2; i < n; i <<= 1) {
auto w0 = w.begin() + i / 2, w1 = w.begin() + i;
Int wn = Int(pow_mod(G, (mod1 - 1) / (i << 1), mod1),
pow_mod(G, (mod2 - 1) / (i << 1), mod2),
pow_mod(G, (mod3 - 1) / (i << 1), mod3));
for (int j = 0; j < i; j += 2)
w1[j] = w0[j >> 1], w1[j + 1] = w1[j] * wn;
}
return w;
} auto w = init_W(1 << 21);
void DIT(Poly &a) {
int n = len(a);
for (int k = n >> 1; k; k >>= 1)
for (int i = 0; i < n; i += k << 1)
for (int j = 0; j < k; ++j) {
Int x = a[i + j], y = a[i + j + k];
a[i + j + k] = (x - y) * w[k + j], a[i + j] = x + y;
}
}
void DIF(Poly &a) {
int n = len(a);
for (int k = 1; k < n; k <<= 1)
for (int i = 0; i < n; i += k << 1)
for (int j = 0; j < k; ++j) {
Int x = a[i + j], y = a[i + j + k] * w[k + j];
a[i + j + k] = x - y, a[i + j] = x + y;
}
Int inv = Int(pow_mod(n, mod1 - 2, mod1), pow_mod(n, mod2 - 2, mod2), pow_mod(n, mod3 - 2, mod3));
for (int i = 0; i < n; ++i) a[i] = a[i] * inv;
reverse(a.begin() + 1, a.end());
}
Poly restore(Poly &a) {
for (int i = 0; i < len(a); ++i) {
int x = a[i].get();
a[i] = Int(x % mod1, x % mod2, x % mod3);
}
return a;
}
Poly operator * (const Poly &A, const Poly &B) {
int n = 1, n1 = len(A), n2 = len(B); while (n < n1 + n2 - 1) n <<= 1; init_P(n);
Poly a(n), b(n); copy_n(A.begin(), n1, a.begin()); copy_n(B.begin(), n2, b.begin());
DIT(a); DIT(b);
for (int i = 0; i < n; ++i) a[i] = a[i] * b[i];
DIF(a); Poly ans(n1 + n2 - 1);
for (int i = 0; i < n1 + n2 - 1; ++i) ans[i] = a[i].get();
return ans;
}
Poly Der(const Poly &a) {
Poly res(a);
for (int i = 0; i < len(a) - 1; ++i) res[i] = (i + 1) * res[i + 1];
res[len(a) - 1] = 0; restore(res); return res;
}
Poly Inte(const Poly &a) {
static int inv[N];
if (!inv[1]) {
inv[1] = 1;
for (int i = 2; i < N; ++i) inv[i] = 1ll * (p - p / i) * inv[p % i] % p;
}
Poly res(a); res.resize(len(a) + 1);
for (int i = len(a); i; --i) res[i] = res[i - 1] * inv[i];
res[0] = 0; restore(res); return res;
}
Poly Inv(const Poly &a) {
Poly res(1, Int(pow_mod(a[0].get(), p - 2, p)));
int n = 1; while (n < len(a)) n <<= 1;
for (int k = 2; k <= n; k <<= 1) {
Poly t(a); t.resize(k);
t = t * res; t.resize(k);
res = res * (2 - t); res.resize(k);
} res.resize(len(a)); restore(res); return res;
}
Poly Ln(const Poly &a) {
Poly res = Inte(Der(a) * Inv(a));
res.resize(len(a)); restore(res); return res;
}
Poly Exp(const Poly &a) {
Poly res(1, 1);
int n = 1; while (n < len(a)) n <<= 1;
for (int k = 2; k <= n; k <<= 1) {
Poly t(res.begin(), res.end()); t.resize(k); t = Ln(t);
for (int i = 0; i < min(len(a), k); ++i) t[i] = a[i] + (p - t[i]); t[0] = t[0] + 1; restore(t);
res = res * t; res.resize(k);
} res.resize(len(a)); restore(res); return res;
}
} // namespace Pol

MTT

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
120
121
122
123
124
125
126
#define Poly vector<int>
#define len(A) ((int) A.size())
namespace Pol {
inline int add(int a, int b) { return (a += b) >= p ? a -= p : a; }
inline int mul(int a, int b) { return 1ll * a * b % p; }
Poly operator - (const int &v, const Poly &a) {
Poly res(a);
for (int i = 0; i < len(res); ++i) res[i] = p - res[i];
res[0] = add(res[0], v); return res;
}
Poly operator - (const Poly &a, const int &v) {
Poly res(a); res[0] = add(res[0], p - v); return res;
}
Poly operator * (const Poly &a, const int &v) {
Poly res(a);
for (int i = 0; i < len(res) ; ++i) res[i] = mul(res[i], v);
return res;
}

const int N = 4200000;
const double pi = acos(-1);
struct Complex {
double x, y;

Complex(double x = 0, double y = 0) : x(x), y(y) {}

friend Complex operator + (const Complex &u, const Complex &v) { return Complex(u.x + v.x, u.y + v.y); }
friend Complex operator - (const Complex &u, const Complex &v) { return Complex(u.x - v.x, u.y - v.y); }
friend Complex operator * (const Complex &u, const Complex &v) {
return Complex(u.x * v.x - u.y * v.y, u.x * v.y + u.y * v.x);
}
}; typedef vector<Complex> Vcp;

int P[N];
void init_P(int n) {
int l = 0; while ((1 << l) < n) ++l;
for (int i = 0; i < n; ++i) P[i] = (P[i >> 1] >> 1) | ((i & 1) << l - 1);
}
vector<Complex> init_W(int n) {
vector<Complex> w(n); w[1] = 1;
for (int i = 2; i < n; i <<= 1) {
auto w0 = w.begin() + i / 2, w1 = w.begin() + i;
Complex wn(cos(pi / i), sin(pi / i));
for (int j = 0; j < i; j += 2)
w1[j] = w0[j >> 1], w1[j + 1] = w1[j] * wn;
}
return w;
} auto w = init_W(1 << 21);
void DIT(Vcp& a) {
int n = len(a);
for (int k = n >> 1; k; k >>= 1)
for (int i = 0; i < n; i += k << 1)
for (int j = 0; j < k; ++j) {
Complex x = a[i + j], y = a[i + j + k];
a[i + j + k] = (x - y) * w[k + j], a[i + j] = x + y;
}
}
void DIF(Vcp &a) {
int n = len(a);
for (int k = 1; k < n; k <<= 1)
for (int i = 0; i < n; i += k << 1)
for (int j = 0; j < k; ++j) {
Complex x = a[i + j], y = a[i + j + k] * w[k + j];
a[i + j + k] = x - y, a[i + j] = x + y;
}
const double inv = 1. / n;
for (int i = 0; i < n; ++i) a[i].x *= inv, a[i].y *= inv;
reverse(a.begin() + 1, a.end());
}
Poly operator *(const Poly &A, const Poly &B) {
int n = 1, n1 = len(A), n2 = len(B); while (n < n1 + n2 - 1) n <<= 1; init_P(n);
if (n1 == 1 && n2 == 1) return Poly { mul(A[0], B[0]) };
Vcp a(n), b(n), c0(n), c1(n);
for (int i = 0; i < n1; ++i) a[i] = Complex(A[i] & 0x7fff, A[i] >> 15);
for (int i = 0; i < n2; ++i) b[i] = Complex(B[i] & 0x7fff, B[i] >> 15);
DIT(a), DIT(b);
for (int k = 1, i = 0, j; k < n; k <<= 1)
for (; i < k * 2; ++i) {
j = i ^ k - 1;
c0[i] = Complex(a[i].x + a[j].x, a[i].y - a[j].y) * b[i] * 0.5;
c1[i] = Complex(a[i].y + a[j].y, -a[i].x + a[j].x) * b[i] * 0.5;
}
DIF(c0), DIF(c1); Poly ans(n1 + n2 - 1);
for (int i = 0; i < n1 + n2 - 1; i++) {
ll c00 = c0[i].x + 0.5, c01 = c0[i].y + 0.5, c10 = c1[i].x + 0.5, c11 = c1[i].y + 0.5;
ans[i] = (c00 + ((c01 + c10) % p << 15) + (c11 % p << 30)) % p;
} return ans;
}
Poly Der(const Poly &a) {
Poly res(a);
for (int i = 0; i < len(a) - 1; ++i) res[i] = mul(i + 1, res[i + 1]);
res[len(a) - 1] = 0; return res;
}
Poly Int(const Poly &a) {
static int inv[N];
if (!inv[1]) {
inv[1] = 1;
for (int i = 2; i < N; ++i) inv[i] = mul(p - p / i, inv[p % i]);
}
Poly res(a); res.resize(len(a) + 1);
for (int i = len(a); i; --i) res[i] = mul(res[i - 1], inv[i]);
res[0] = 0; return res;
}
Poly Inv(const Poly &a) {
Poly res(1, pow_mod(a[0], p - 2));
int n = 1; while (n < len(a)) n <<= 1;
for (int k = 2; k <= n; k <<= 1) {
int L = 2 * k; init_P(L); Poly t(k);
copy_n(a.begin(), min(k, len(a)), t.begin());
t = 2 - t * res; res = res * t; res.resize(k);
} res.resize(len(a)); return res;
}
Poly Ln(const Poly &a) {
Poly res = Int(Der(a) * Inv(a));
res.resize(len(a)); return res;
}
Poly Exp(const Poly &a) {
Poly res(1, 1);
int n = 1; while (n < len(a)) n <<= 1;
for (int k = 2; k <= n; k <<= 1) {
Poly t(res.begin(), res.end()); t.resize(k); t = Ln(t);
for (int i = 0; i < min(len(a), k); ++i) t[i] = add(a[i], p - t[i]); t[0] = add(t[0], 1);
res = res * t; res.resize(k);
} res.resize(len(a)); return res;
}
} // namespace Pol

多项式乘法

多项式平移

定义:对于一个多项式 $F(x)$,求 $G(x)\equiv F(x+c)(\bmod x^n)$,其中 $c$ 为常数

做法:不妨令 $F(x)=\sum_{i=0}^n f_ix^i$

容易发现是一个差卷积的形式

1
2
3
4
5
6
7
8
9
Poly Offset(const Poly &a, int c) {
int n = len(a); init_C();
Poly t1(n), t2(n);
for (int i = 0; i < n; ++i) t1[i] = mul(pow_mod(c, i), inv[i]);
for (int i = 0; i < n; ++i) t2[i] = mul(a[i], fac[i]);
t1 = MMul(t1, t2);
for (int i = 0; i < n; ++i) t1[i] = mul(t1[i], inv[i]);
return t1;
}

多项式乘法逆

定义:对于一个多项式 $F(x)$,如果存在一个多项式 $G(x)$ 满足 $deg(G)\le deg(A)$,且 $F(x)G(x)\equiv 1(\bmod x^n)$​,则称 $G(x)$ 为 $F(x)$ 在模 $x^n$ 意义下的逆元

做法:假设我们现在已经有了 $F(x)G(x)\equiv 1(\bmod x^n)$,我们考虑求出 $F(x)G(x)\equiv 1(\bmod x^{2n})$​

我们移项得到 $F(x)G(x)-1\equiv 0(\bmod x^n)$,然后我们考虑平方可以得到 $(F(x)G(x)-1)^2\equiv 0(\bmod x^{2n}$,展开有 $F(x)(2G(x)-F(x)G^2(x))\equiv 1(\bmod x^{2n})$​ 一路倍增即可,时间复杂度为 $T(n)=T(\frac n 2)+O(n\log n)=O(n\log n)$

1
2
3
4
5
6
7
8
9
10
11
12
Poly Inv(const Poly &a) {
Poly res(1, pow_mod(a[0], p - 2));
int n = 1; while (n < len(a)) n <<= 1;
for (int k = 2; k <= n; k <<= 1) {
int L = 2 * k; init_P(L); Poly t(L); // F(x)G^2(x) 的长度是 3k
copy_n(a.begin(), min(k, len(a)), t.begin());
t.resize(L); res.resize(L);
NTT(res, 1); NTT(t, 1);
for (int i = 0; i < L; ++i) res[i] = mul(res[i], add(2, p - mul(t[i], res[i])));
NTT(res, -1); res.resize(k);
} res.resize(len(a)); return res;
}

多项式牛顿迭代

定义:已知多项式 $F(x)$,求 $G(x)$,满足 $F(G(x))\equiv 0(\bmod x^n)$

做法:我们仍考虑倍增的做法,不妨假设我们现在已经有一个 $G_{k-1}(x)$,满足 $F(G_{k-1}(x))\equiv 0(\bmod x^{2^{k-1}})$,现在我们要求 $G_k(x)$,我们将 $F(G_k(x))$ 在 $G_{k-1}(x)$ 处展开,得到 $F(G_k(x))=\sum_{i=0}^\infty\frac{F^{(i)}(G_{k-1}(x))}{i!}(G_{k}(x)-G_{k-1}(x))^i$

注意到在模 $2^k$ 下,$i$ 大于等于 $2$ 的 $(G_k(x)-G_{k-1}(x))^i$ 都是 $0$,那么就能得到 $G_k(x)=G_{k-1}(x)-\frac{F(G_{k-1}(x))}{F’(G_{k-1}(x))}$

多项式对数函数

定义:多项式对数函数,我们认为是一个多项式和麦克劳林级数的复合,即对于多项式 $F(x)=\sum_{i=1}^{\infty}a_ix^i$​,有 $\ln(1-F(x))=-\sum_{i=0}^{\infty}\frac{F^i(x)}{i}$​​

已知多项式 $F(x)$​,如果存在一个多项式 $G(x)\equiv \ln F(x)(\bmod x^n)$​,则称 $G(x)$​​ 为多项式 $F(x)$​ 的对数,保证 $f_0=1$

做法:我们对于原式两边求导 $G’(x)\equiv \frac{F’(x)}{F(x)}(\bmod x^n)$,直接求逆即可​

1
2
3
4
Poly Ln(const Poly &a) {
Poly res = Int(Der(a) * Inv(a));
res.resize(len(a)); return res;
}

多项式指数函数

定义:已知多项式 $F(x)$,求 $G(x)$,满足 $e^{F(x)}\equiv G(x)(\bmod x^n)$,保证 $f_0=0$

做法:我们对两边取 $\ln$,得到 $\ln G(x)-F(x)\equiv 0$,套用牛顿迭代的式子,即令 $H(G(x))=\ln G(x)-F(x)$,得到$G_k(x)\equiv G_{k-1}(x)(1-\ln G_{k-1}(x)+F(x))$,时间复杂度 $O(n\log n)$

1
2
3
4
5
6
7
8
9
Poly Exp(const Poly &a) {
Poly res(1, 1);
int n = 1; while (n < len(a)) n <<= 1;
for (int k = 2; k <= n; k <<= 1) {
Poly t(res.begin(), res.end()); t.resize(k); t = Ln(t);
for (int i = 0; i < min(len(a), k); ++i) t[i] = add(a[i], p - t[i]); t[0] = add(t[0], 1);
res = res * t; res.resize(k);
} res.resize(len(a)); return res;
}

多项式开根

定义:已知多项式 $F(x)$,求 $G(x)$,满足 $F(x)\equiv G^2(x)(\bmod x^n)$,如果 $f_0\neq 1$,需要用二次剩余来求解 $g_0$

做法:我们移项后直接套用牛顿迭代即可,$H(G(x))=G^2(x)-F(x)$,得到 $G_k(x)\equiv \frac{1}{2}(G_{k-1}(x)-\frac{F(x)}{G_{k-1}(x)})$,时间复杂度 $O(n\log n)$

1
2
3
4
5
6
7
8
9
Poly Sqrt(const Poly &a) { // a[0] = 1
Poly res(1, 1); ll inv2 = pow_mod(2, p - 2);
int n = 1; while (n < len(a)) n <<= 1;
for (int k = 2; k <= n; k <<= 1) {
Poly t(res.begin(), res.end()), ta(a.begin(), a.begin() + min(len(a), k));
t.resize(k); t = Inv(t) * ta;
res.resize(k); for (int i = 0; i < k; ++i) res[i] = mul(add(res[i], t[i]), inv2);
} res.resize(len(a)); return res;
}

多项式快速幂

定义:已知多项式 $F(x)$ 和非负整数 $k$,求 $G(x)$,满足 $F^k(x)\equiv G(x)(\bmod x^n)$

做法:两边取 $\ln$ 之后再 $\exp$,得到 $G(x)\equiv \exp(k\ln F(x))$,如果 $f_0$ 等于 $1$,那么我们这样已经做完了,如果 $f_0>1$,那么我们考虑将整体除掉 $f_0$,化为 $f_0=1$ 的情况,如果 $f_0=0$,那么我们需要找一个 $t$,那么 $f_t>0$,且 $f_0$ 到 $f_{t-1}$ 都是 $0$,然后除掉 $f_tx^t$ 再做快速幂即可,细节较多,常数较大,时间复杂度 $O(n\log n)$

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
Poly Pow(const Poly &a, int k) { // a[0] = 1
return Exp(Ln(a) * k);
}
Poly Pow(const Poly &a, int k, int kk) {
// 模数如果 < long long, 即可以直接存储, kk = k; 否则 k 是对 p 取模的结果, kk 是对 p - 1 取模的结果
int n = len(a), t = n, m, v, inv, powv; Poly res(n);
for (int i = n - 1; ~i; --i) if (a[i]) t = i, v = a[i];
if (k && t >= (n + k - 1) / k) return res;
if (t == n) { if (!k) res[0] = 1; return res; }
m = n - t * k; res.resize(m);
inv = pow_mod(v, p - 2); powv = pow_mod(v, kk);
for (int i = 0; i < m; ++i) res[i] = mul(a[i + (k > 0) * t], inv);
res = Exp(Ln(res) * k); res.resize(n);
for (int i = m - 1; ~i; --i) {
ll tmp = mul(res[i], powv);
res[i] = 0, res[i + t * k] = tmp;
}
return res;
}
int main() {
cin >> n >> s + 1; int len = strlen(s + 1), t = n; Poly A(n);
for (int i = 1; i <= len; ++i)
k = (10ll * k + s[i] - '0') % p, kk = (10ll * kk + s[i] - '0') % (p - 1);
for (int i = 0; i < n; ++i) cin >> A[i];
for (int i = n - 1; ~i; --i) if (A[i]) t = i;
if (len > 5 && t) {
for (int i = 0; i < n; ++i) cout << 0 << " \n"[i == n - 1];
return 0;
} Poly res = Pol::Pow(A, k, kk);
for (int i = 0; i < n; ++i) cout << res[i] << " \n"[i == n - 1];
return 0;
}

多项式带余除法

定义:已知一个 $n$ 次多项式 $F(x)$ 和一个 $m(m\le n)$ 次多项式 $G(x)$,要求出一个 $n-m+1$ 次多项式 $Q(x)$ 和一个次数小于 $m$ 的多项式 $R(x)$,满足 $F(x)=Q(x)G(x)+R(x)$

做法:感觉已经超脱了多项式的领域

我们带入 $\frac{1}{x}$ 得:$F(\frac{1}{x})=Q(\frac 1 x)G(\frac 1 x)+R(\frac 1 x)$,两边同乘 $x^{n-1}$ 得:$x^{n-1}F(\frac 1 x)=x^{n-m}Q(\frac 1x)x^{m-1}G(\frac 1x)+x^nR(\frac 1x)$

我们令 $F^{R}(x)$ 表示将 $F(x)$ 的系数翻转,那么之前的式子等价于 $F^R(x)=Q^R(x)G^R(x)+x^{n-m+1}R^R(x)$

那么 $F^R(x)\equiv Q^R(x)G^R(x)(\bmod x^{n-m+1})$,那么 $Q^R(x)$ 就是求个逆元的问题,求出 $Q(x)$ 之后减一下就能得到 $R(x)$ 了,时间复杂度 $O(n\log n)$

1
2
3
4
5
6
7
pair<Poly, Poly> Divide(const Poly &a, const Poly &b) {
int n = len(a), m = len(b);
Poly t1(a.rbegin(), a.rbegin() + n - m + 1), t2(b.rbegin(), b.rend()); t2.resize(n - m + 1);
Poly Q = Inv(t2) * t1; Q.resize(n - m + 1); reverse(Q.begin(), Q.end());
Poly R = Q * b; R.resize(m - 1); for (int i = 0; i < len(R); ++i) R[i] = add(a[i], p - R[i]);
return make_pair(Q, R);
}

多项式经典结论

  1. 多项式的幂次

拉格朗日插值

$n$ 个点可唯一确定一个 $n-1$ 次多项式,而拉格朗日差值就是用来根据这 $n$ 个点来确定这个多项式的算法

推导过程

我们令

容易得到当且仅当我们带入点 $(x_k,y_k)$,$f_k(x)$ 才能等于 $y_k$,其它情况均等于 $0$

那么可以得到这个多项式 $f(x)=\sum_{i=1}^{n}f_i(x)$,这个式子不是太好看,我们稍微提一下公共项

令 $l(x)=\prod_{i=1}^{n}(x-x_i)$,$\omega_k=\frac{y_k}{\prod_{i\neq k}(x_k-x_i)}$,那么 $f(x)=l(x)\sum_{i=1}^{n}\frac{\omega_i}{(x-x_i)}$

模板

对于任意给定的 $n+1$ 个点,我们可以做到 $O(n^2)$ 求系数和求值

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
void Lagrange(int *res, int *x, int *y, int n) {
static int a[maxn], b[maxn];
fill(a, a + n, 0); a[0] = 1;
for (int i = 1; i <= n; ++i) {
for (int j = i; j; --j)
a[j] = (a[j - 1] - 1ll * x[i] * a[j]) % p;
a[0] = -1ll * x[i] * a[0] % p;
} b[n] = 0;
for (int i = 1; i <= n; ++i) {
for (int j = n - 1; ~j; --j)
b[j] = (a[j + 1] + 1ll * x[i] * b[j + 1]) % p;
int mul = 1;
for (int j = 1; j <= n; ++j)
if (i != j) mul = 1ll * mul * (x[i] - x[j]) % p;
mul = pow_mod(mul, p - 2) * y[i] % p;
for (int j = 0; j < n; ++j) res[j] = (res[j] + 1ll * b[j] * mul) % p;
}
}

// O(n^2)
#define Poly vector<int>
Poly Lagrange(const Poly &x, const Poly &y, int n) { // i in [1, n], n >= 2
Poly a(n + 1), b(n + 1); a[0] = 1;
for (int i = 1; i <= n; ++i) {
for (int j = i; j; --j) a[j] = add(a[j - 1], p - mul(x[i], a[j]));
a[0] = p - mul(x[i], a[0]);
} Poly res(n);
for (int i = 1; i <= n; ++i) {
for (int j = n - 1; ~j; --j) b[j] = add(a[j + 1], mul(x[i], b[j + 1]));
int t = 1;
for (int j = 1; j <= n; ++j)
if (i != j) t = mul(t, add(x[i], p - x[j]));
t = mul(pow_mod(t, p - 2), y[i]);
for (int j = 0; j < n; ++j) res[j] = add(res[j], mul(b[j], t));
} return res;
}

// O(n)
#define Poly vector<int>
int Lagrange(const Poly &y, int n, int k) { // xi = i, i in [1, n], n >= 2
init_C(n); vector<int> pl(n + 1), sl(n + 2);
pl[0] = 1; for (int i = 1; i <= n; ++i) pl[i] = mul(pl[i - 1], add(k, p - i));
sl[n + 1] = 1; for (int i = n; i; --i) sl[i] = mul(sl[i + 1], add(k, p - i));
int ans = 0;
for (int i = 1; i <= n; ++i)
ans = add(ans, mul({ pl[i - 1], sl[i + 1], y[i], inv[i - 1], (n - i & 1) ? p - inv[n - i] : inv[n - i] }));
return ans;
}

动态加点可以做单次 $O(n)$​​ 求值和系数

如果 $x_i$ 连续,可以做到一次 $O(n)$​ 求值