多项式半家桶
曾经有人说过自己这辈子不会遇到多项式科技,所以他没有学,直到模拟赛遇到了多项式开根。
本文将依托多项式乘法,介绍几种常见的多项式科技。因为笔者太菜,所以学的不多,以后可能会补充。
1 多项式乘法逆
1.1 过程
模板题:【模板】多项式乘法逆。
我们现在要求出 \(F\) 在模 \(x^n\) 意义下的逆,考虑递归求解。假如我们当前已经求出了模 \(x^p\) 的逆,现在要推出模 \(x^{2p}\) 的逆。设 \(F\) 在模 \(x^p\) 下的逆元为 \(B'\),模 \(x^{2p}\) 的逆元为 \(B\),那么我们有:
\[\begin{aligned}
&F\times B'\equiv 1\pmod {x^p}\\
&F\times B\equiv 1\pmod {x^{2p}}
\end{aligned}
\]
那么容易得到 \((B'-B)\equiv 0\pmod {x^p}\)。然后两边同时平方,注意模数的次数也要平方:
\[\begin{aligned}
&(B'-B)^2\equiv 0\pmod {x^{2p}}\\
\Rightarrow &{B'}^2-2BB'+B^2\equiv 0\pmod {x^{2p}}
\end{aligned}
\]
然后左右两边同时乘 \(F\),这样 \(F\times B\) 就没了:
\[A{B'}^2-2B'+B\equiv 0\pmod {x^{2p}}
\]
移项得:
\[B\equiv 2B'-A{B'}^2 \pmod {x^{2p}}
\]
这样的话我们就得出了 \(B\) 的递推式,倍增处理即可。最后我们求出来的是模 \(x^{2^p}\) 的逆元,显然如果 \(2^p\ge n\) 那么它也是模 \(x^n\) 下的逆元,所以直接求出来的就是答案。
利用 NTT 计算多项式乘法,根据主定理,复杂度是 \(O(n\log n)\) 的。
1.2 代码
模板题代码如下:
#include <bits/stdc++.h>
#define il inline
using namespace std;
const int Maxn = (1 << 18) + 1;
const int Inf = 2e9;
const int Mod = 998244353;
const int YG = 3, InvG = 332748118;
il int Add(int x, int y) {return x + y >= Mod ? x + y - Mod: x + y;} il void pls(int &x, int y) {x = Add(x, y);}
il int Del(int x, int y) {return x - y < 0 ? x - y + Mod : x - y;} il void sub(int &x, int y) {x = Del(x, y);}
il int qpow(int a, int b, int P = Mod) {int res = 1; for(; b; a = 1ll * a * a % P, b >>= 1) if(b & 1) res = 1ll * res * a % P; return res;}
il int Inv(int a) {return qpow(a, Mod - 2);}
template <typename T> il void chkmax(T &x, T y) {x = (x >= y ? x : y);}
template <typename T> il void chkmin(T &x, T y) {x = (x <= y ? x : y);}
template <typename T>
il void read(T &x) {
x = 0; char ch = getchar(); bool flg = 0;
for(; ch < '0' || ch > '9'; ch = getchar()) flg = (ch == '-');
for(; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
flg ? x = -x : 0;
}
template <typename T>
il void write(T x, bool typ = 1) {
static short Stk[50], Top = 0;
x < 0 ? putchar('-'), x = -x : 0;
do Stk[++Top] = x % 10, x /= 10; while(x);
while(Top) putchar(Stk[Top--] | 48);
typ ? putchar('\n') : putchar(' ');
}
il void IOS() {ios::sync_with_stdio(0); cin.tie(0), cout.tie(0);}
il void File() {freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout);}
bool Beg;
int n, F[Maxn], G[Maxn];
int lim = 1;
int r[Maxn];
il void NTT(int *a, int n, int typ) {
for(int i = 0; i < n; i++) if(i < r[i]) swap(a[i], a[r[i]]);
for(int h = 1; h < n; h <<= 1) {
int cur = qpow(typ == 1 ? YG : InvG, (Mod - 1) / (h << 1));
for(int i = 0; i < n; i += (h << 1)) {
int w = 1;
for(int j = 0; j < h; j++, w = 1ll * w * cur % Mod) {
int x = a[i + j], y = 1ll * a[i + j + h] * w % Mod;
a[i + j] = Add(x, y);
a[i + j + h] = Del(x, y);
}
}
}
if(typ == -1) {
int iv = Inv(n);
for(int i = 0; i < n; i++) a[i] = 1ll * a[i] * iv % Mod;
}
}
int A[Maxn], B[Maxn];
il void Inv(int *a, int *b, int n) {
b[0] = Inv(a[0]);
for(int lim = 2; lim <= n; lim <<= 1) {
int len = lim << 1;
for(int i = 0; i < lim; i++) A[i] = a[i], B[i] = b[i];
for(int i = 0; i < len; i++) r[i] = (r[i >> 1] >> 1) | ((i & 1) * (len >> 1));
NTT(A, len, 1), NTT(B, len, 1);
for(int i = 0; i < len; i++) b[i] = 1ll * B[i] * Del(2, 1ll * A[i] * B[i] % Mod) % Mod;
NTT(b, len, -1);
for(int i = lim; i < len; i++) b[i] = 0;
}
}
bool End;
il void Usd() {cerr << (&Beg - &End) / 1024.0 / 1024.0 << "MB " << (double)clock() * 1000.0 / CLOCKS_PER_SEC << "ms\n"; }
int main() {
read(n);
for(int i = 0; i < n; i++) read(F[i]);
while(lim <= n) lim <<= 1;
Inv(F, G, lim);
for(int i = 0; i < n; i++) write(G[i], 0);
Usd();
return 0;
}
2 多项式开根
2.1 过程
模板题:【模板】多项式开根。
这个过程和多项式乘法逆非常相似,考虑递归求解。设 \(F\) 在模 \(x^p\) 下开根为 \(B'\),模 \(x^{2p}\) 下开根为 \(B\),那么我们有:
\[\begin{aligned}
& {B'}^2\equiv F\pmod {x^p}\\
& {B}^2\equiv F\pmod {x^{2p}}
\end{aligned}
\]
那么容易得到 \((B'-B)\equiv 0\pmod {x^p}\)。然后两边同时平方,注意模数的次数也要平方:
\[\begin{aligned}
&(B'-B)^2\equiv 0\pmod {x^{2p}}\\
\Rightarrow &{B'}^2-2BB'+B^2\equiv 0\pmod {x^{2p}}
\end{aligned}
\]
然后把平方项换掉:
\[{B'}^2-2BB'+F\equiv 0\pmod {x^{2p}}
\]
移项得:
\[B\equiv \frac{{B'}^2+F}{2B'}\equiv\frac{B'}{2}+\frac{F}{B'} \pmod {x^{2p}}
\]
这样的话我们就得出了 \(B\) 的递推式,用多项式求逆解决即可。根据主定理,复杂度是 \(O(n\log n)\) 的。
2.2 代码
#include <bits/stdc++.h>
#define il inline
using namespace std;
const int Maxn = (1 << 18) + 1;
const int Inf = 2e9;
const int Mod = 998244353;
const int YG = 3, InvG = 332748118, Inv2 = 499122177;
il int Add(int x, int y) {return x + y >= Mod ? x + y - Mod: x + y;} il void pls(int &x, int y) {x = Add(x, y);}
il int Del(int x, int y) {return x - y < 0 ? x - y + Mod : x - y;} il void sub(int &x, int y) {x = Del(x, y);}
il int qpow(int a, int b, int P = Mod) {int res = 1; for(; b; a = 1ll * a * a % P, b >>= 1) if(b & 1) res = 1ll * res * a % P; return res;}
il int Inv(int a) {return qpow(a, Mod - 2);}
template <typename T> il void chkmax(T &x, T y) {x = (x >= y ? x : y);}
template <typename T> il void chkmin(T &x, T y) {x = (x <= y ? x : y);}
template <typename T>
il void read(T &x) {
x = 0; char ch = getchar(); bool flg = 0;
for(; ch < '0' || ch > '9'; ch = getchar()) flg = (ch == '-');
for(; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
flg ? x = -x : 0;
}
template <typename T>
il void write(T x, bool typ = 1) {
static short Stk[50], Top = 0;
x < 0 ? putchar('-'), x = -x : 0;
do Stk[++Top] = x % 10, x /= 10; while(x);
while(Top) putchar(Stk[Top--] | 48);
typ ? putchar('\n') : putchar(' ');
}
il void IOS() {ios::sync_with_stdio(0); cin.tie(0), cout.tie(0);}
il void File() {freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout);}
bool Beg;
int n, F[Maxn], G[Maxn];
int lim = 1;
int r[Maxn];
il void NTT(int *a, int n, int typ) {
for(int i = 0; i < n; i++) if(i < r[i]) swap(a[i], a[r[i]]);
for(int h = 1; h < n; h <<= 1) {
int cur = qpow(typ == 1 ? YG : InvG, (Mod - 1) / (h << 1));
for(int i = 0; i < n; i += (h << 1)) {
int w = 1;
for(int j = 0; j < h; j++, w = 1ll * w * cur % Mod) {
int x = a[i + j], y = 1ll * a[i + j + h] * w % Mod;
a[i + j] = Add(x, y);
a[i + j + h] = Del(x, y);
}
}
}
if(typ == -1) {
int iv = Inv(n);
for(int i = 0; i < n; i++) a[i] = 1ll * a[i] * iv % Mod;
}
}
int A[Maxn], B[Maxn];
il void Inv(int *a, int *b, int n) {
b[0] = Inv(a[0]);
for(int lim = 2; lim < (n << 1); lim <<= 1) {
int len = lim << 1;
for(int i = 0; i < lim; i++) A[i] = a[i], B[i] = b[i];
for(int i = 0; i < len; i++) r[i] = (r[i >> 1] >> 1) | ((i & 1) * (len >> 1));
NTT(A, len, 1), NTT(B, len, 1);
for(int i = 0; i < len; i++) b[i] = 1ll * B[i] * Del(2, 1ll * A[i] * B[i] % Mod) % Mod;
NTT(b, len, -1);
for(int i = lim; i < len; i++) b[i] = 0;
}
for(int i = 0; i <= (n << 1); i++) A[i] = B[i] = 0;
}
int C[Maxn], D[Maxn];
il void Sqrt(int *a, int *b, int n) {
b[0] = 1;
for(int lim = 2; lim < (n << 1); lim <<= 1) {
int len = lim << 1;
for(int i = 0; i < lim; i++) C[i] = a[i];
Inv(b, D, lim);
for(int i = 0; i < len; i++) r[i] = (r[i >> 1] >> 1) | ((i & 1) * (len >> 1));
NTT(C, len, 1), NTT(D, len, 1);
for(int i = 0; i < len; i++) C[i] = 1ll * C[i] * D[i] % Mod;
NTT(C, len, -1);
for(int i = 0; i < lim; i++) b[i] = 1ll * Add(b[i], C[i]) * Inv2 % Mod;
}
for(int i = 0; i <= (n << 1); i++) C[i] = D[i] = 0;
}
bool End;
il void Usd() {cerr << (&Beg - &End) / 1024.0 / 1024.0 << "MB " << (double)clock() * 1000.0 / CLOCKS_PER_SEC << "ms\n"; }
int main() {
read(n);
for(int i = 0; i < n; i++) read(F[i]);
while(lim <= n) lim <<= 1;
Sqrt(F, G, lim);
for(int i = 0; i < n; i++) write(G[i], 0);
Usd();
return 0;
}

浙公网安备 33010602011771号