多项式的各种操作
前置知识:多项式乘法FFT/NTT
多项式求逆
F ( x ) G ( x ) ≡ 1 ( m o d x n ) F(x)G(x) ≡ 1( mod \ x^n) F(x)G(x)≡1(mod xn)
已知 F ( x ) F(x) F(x)求 G ( x ) G(x) G(x)
解法:
假设已经求出了
F
(
x
)
G
′
(
x
)
≡
1
(
m
o
d
x
⌈
n
2
⌉
)
F(x)G'(x) ≡ 1( mod x^{\lceil {\frac{n}{2}} \rceil})
F(x)G′(x)≡1(modx⌈2n⌉)
显然
F
(
x
)
G
(
x
)
≡
1
(
m
o
d
x
⌈
n
2
⌉
)
F(x)G(x) ≡ 1( mod \ x^{\lceil {\frac{n}{2}} \rceil})
F(x)G(x)≡1(mod x⌈2n⌉)
拿下面那个减一下上面那个
F ( x ) ( G ( x ) − G ′ ( x ) ) ≡ 0 ( m o d x ⌈ n 2 ⌉ ) F(x)(G(x) - G'(x)) ≡ 0( mod \ x^{\lceil {\frac{n}{2}} \rceil}) F(x)(G(x)−G′(x))≡0(mod x⌈2n⌉)
因为
F
(
x
)
不
同
余
0
(
m
o
d
x
⌈
n
2
⌉
)
F(x) 不同余 0( mod \ x^{\lceil {\frac{n}{2}} \rceil})
F(x)不同余0(mod x⌈2n⌉)
所以
G
(
x
)
−
G
′
(
x
)
≡
0
(
m
o
d
x
⌈
n
2
⌉
)
G(x) - G'(x) ≡ 0( mod \ x^{\lceil {\frac{n}{2}} \rceil})
G(x)−G′(x)≡0(mod x⌈2n⌉)
然后很naive地把它左右平方
G ( x ) 2 + G ′ ( x ) 2 − 2 G ( x ) G ′ ( x ) ≡ 0 ( m o d x n ) G(x) ^ 2 + G'(x) ^ 2 - 2 G(x)G'(x)≡ 0( mod \ x^n) G(x)2+G′(x)2−2G(x)G′(x)≡0(mod xn)
再乘 F ( x ) F(x) F(x)
G ( x ) + G ′ ( x ) 2 F ( x ) − 2 G ′ ( x ) ≡ 0 ( m o d x n ) G(x) + G'(x) ^ 2F(x) - 2G'(x)≡ 0( mod \ x^n) G(x)+G′(x)2F(x)−2G′(x)≡0(mod xn)
移项
G
(
x
)
≡
2
G
′
(
x
)
−
G
′
(
x
)
2
F
(
x
)
(
m
o
d
x
n
)
G(x) ≡ 2G'(x) - G'(x) ^ 2F(x) ( mod \ x^n)
G(x)≡2G′(x)−G′(x)2F(x)(mod xn)
然后就直接做就好了
根据主定理这个东东的之间复杂度是 T ( n ) = T ( n / 2 ) + n l o g n = n l o g n T(n) = T(n/2) + n log n =n log n T(n)=T(n/2)+nlogn=nlogn
code:
#include<bits/stdc++.h>
#define int long long
#define mod 998244353
#define G 3
#define N 8000005
using namespace std;
int qpow(int x, int y){
int ret = 1;
for(; y; y >>= 1, x = x * x % mod) if(y & 1) ret = ret * x % mod;
return ret;
}
int rev[N], G_inv, len_inv;
void ntt(int *a, int len, int o){//NTT板子
len_inv = qpow(len, mod - 2);
for(int i = 0; i <= len; i ++) rev[i] = (rev[i >> 1] >> 1) | ((i&1) * len >> 1);
for(int i = 0; i <= len; i ++) if(i < rev[i]) swap(a[i], a[rev[i]]);
for(int i = 2; i <= len; i <<= 1){
int wn = qpow((o == 1)? G:G_inv, (mod - 1) / i);
for(int j = 0, p = i / 2; j + i - 1 <= len; j += i){
int w0 = 1;
for(int k = j; k < j + p; k ++, w0 = w0 * wn % mod){
int X = a[k], Y = w0 * a[k + p] % mod;
a[k] = (X + Y) % mod;
a[k + p] = (X - Y + mod) % mod;
}
}
}
if(o == -1)
for(int i = 0; i <= len; i ++) a[i] = a[i] * len_inv % mod;
}
int c[N];
void inv(int *a, int *b, int sz){
if(sz == 1) {b[0] = qpow(a[0], mod - 2); return;}//常数直接求逆元
inv(a, b, (sz + 1) / 2);//把小的先求出来
int len = 1;
for(; len <= sz + sz; len <<= 1);
for(int i = 0; i < sz; i ++) c[i] = a[i];//注意要用c替换一下,不然a会做很多次NTT
for(int i = sz; i <= len; i ++) c[i] = 0;
ntt(c, len, 1), ntt(b, len, 1);
for(int i = 0; i <= len; i ++) b[i] = (b[i] * 2 % mod - b[i] * b[i] % mod * c[i] % mod + mod) % mod;//直接算
ntt(b, len, -1);
for(int i = sz; i <= len; i ++) b[i] = 0;//注意,b要用很多次,所以要把后面的清空一下
}
int a[N], b[N], n, m;
signed main(){
G_inv = qpow(G, mod - 2);
scanf("%lld", &n);
for(int i = 0; i < n; i ++) scanf("%lld", &a[i]);
inv(a, b, n);
for(int i = 0; i < n; i ++) printf("%lld ", b[i]);
return 0;
}
多项式除法
F
(
x
)
最
高
次
是
n
,
G
(
x
)
最
高
次
是
m
,
则
Q
(
x
)
最
高
次
是
n
−
m
,
R
最
高
次
是
m
−
1
F(x)最高次是n, G(x)最高次是m, 则Q(x)最高次是n-m, R最高次是m-1
F(x)最高次是n,G(x)最高次是m,则Q(x)最高次是n−m,R最高次是m−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)
把 1 x \frac{1}{x} x1代入
F ( 1 x ) = Q ( 1 x ) G ( 1 x ) + R ( 1 x ) F(\frac{1}{x})=Q(\frac{1}{x})G(\frac{1}{x})+R(\frac{1}{x}) F(x1)=Q(x1)G(x1)+R(x1)
x n F ( 1 x ) = x n Q ( 1 x ) G ( 1 x ) + x n R ( 1 x ) x^nF(\frac{1}{x})=x^nQ(\frac{1}{x})G(\frac{1}{x})+x^nR(\frac{1}{x}) xnF(x1)=xnQ(x1)G(x1)+xnR(x1)
然后发现 x n F ( 1 x ) = F R ( x ) 即 将 F 的 系 数 前 后 翻 转 一 下 x^nF(\frac{1}{x}) = F_R(x)\ \ \ 即将F的系数前后翻转一下 xnF(x1)=FR(x) 即将F的系数前后翻转一下
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 ) x^nF(\frac{1}{x})=x^{n - m}Q(\frac{1}{x})x^mG(\frac{1}{x})+x^{n-m+1}x^{m-1}R(\frac{1}{x}) xnF(x1)=xn−mQ(x1)xmG(x1)+xn−m+1xm−1R(x1)
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)+x^{n-m+1}R_R(x) FR(x)=QR(x)GR(x)+xn−m+1RR(x)
两边
m
o
d
x
n
−
m
+
1
\bmod \ \ \ x^{n-m+1}
mod xn−m+1
F
R
(
x
)
=
Q
R
(
x
)
G
R
(
x
)
(
m
o
d
x
n
−
m
+
1
)
F_R(x)=Q_R(x)G_R(x)(mod \ \ \ x^{n-m+1})
FR(x)=QR(x)GR(x)(mod xn−m+1)
因为
Q
(
x
)
的
最
高
次
是
n
−
m
<
n
−
m
+
1
Q(x) 的最高次是 n-m < n-m+1
Q(x)的最高次是n−m<n−m+1所以没关系(但注意要补齐)
然后变一下形
Q
R
(
x
)
=
F
R
(
x
)
G
R
−
1
(
x
)
(
m
o
d
x
n
−
m
+
1
)
Q_R(x)=F_R(x)G_R^{-1}(x)(mod \ \ \ x^{n-m+1})
QR(x)=FR(x)GR−1(x)(mod xn−m+1)
求 G R ( x ) 的 逆 元 即 可 G_R(x)的逆元即可 GR(x)的逆元即可
R ( x ) = F ( x ) − G ( x ) Q ( x ) R(x) = F(x) - G(x)Q(x) R(x)=F(x)−G(x)Q(x)
#include<bits/stdc++.h>
#define int long long
#define mod 998244353
#define GG 3
#define N 8000005
using namespace std;
int qpow(int x, int y){
int ret = 1;
for(; y; y >>= 1, x = x * x % mod) if(y & 1) ret = ret * x % mod;
return ret;
}
int rev[N], GG_inv, len_inv;
void ntt(int *a, int len, int o){//NTT板子
len_inv = qpow(len, mod - 2);
for(int i = 0; i <= len; i ++) rev[i] = (rev[i >> 1] >> 1) | ((i&1) * len >> 1);
for(int i = 0; i <= len; i ++) if(i < rev[i]) swap(a[i], a[rev[i]]);
for(int i = 2; i <= len; i <<= 1){
int wn = qpow((o == 1)? GG:GG_inv, (mod - 1) / i);
for(int j = 0, p = i / 2; j + i - 1<= len; j += i){
int w0 = 1;
for(int k = j; k < j + p; k ++, w0 = w0 * wn % mod){
int X = a[k], Y = w0 * a[k + p] % mod;
a[k] = (X + Y) % mod;
a[k + p] = (X - Y + mod) % mod;
}
}
}
if(o == -1)
for(int i = 0; i <= len; i ++) a[i] = a[i] * len_inv % mod;
}
int c[N];
void inv(int *a, int *b, int sz){//多项式求逆板子
if(sz == 0) {b[0] = qpow(a[0], mod - 2); return;}
inv(a, b, sz / 2);
int len = 1;
for(; len <= sz + sz; len <<= 1);
for(int i = 0; i <= sz; i ++) c[i] = a[i];
for(int i = sz + 1; i <= len; i ++) c[i] = 0;
ntt(c, len, 1), ntt(b, len, 1);
for(int i = 0; i <= len; i ++) b[i] = (b[i] * 2 % mod - b[i] * b[i] % mod * c[i] % mod + mod) % mod;
ntt(b, len, -1);
for(int i = sz + 1; i <= len; i ++) b[i] = 0;
}
void mul(int *a, int *b, int n, int m){//多项式乘法板子
int len = 1;
for(; len <= n + m; len <<= 1);
ntt(a, len, 1), ntt(b, len, 1);
for(int i = 0; i <= len; i ++) a[i] = a[i] * b[i] % mod;
ntt(a, len, -1);
}
int a[N], b[N], F[N], G[N], Q[N], GR[N], FR[N], GR_inv[N], R[N], n, m;
signed main(){
GG_inv = qpow(GG, mod - 2);
scanf("%lld%lld", &n, &m);
for(int i = 0; i <= n; i ++) scanf("%lld", &F[i]), FR[n - i] = F[i];
for(int i = 0; i <= m; i ++) scanf("%lld", &G[i]), GR[m - i] = G[i];
for(int i = n - m + 1; i <= m; i ++) GR[i] = 0;// mod n - m + 1, 即把后面的全部清0
inv(GR, GR_inv, n - m);//注意长度是 n - m, 要补齐
mul(FR, GR_inv, n, n - m); //长度要补齐
for(int i = 0; i <= n - m; i ++) Q[i] = FR[n - m - i];//翻转
for(int i = 0; i <= n - m; i ++) printf("%lld ", Q[i]); printf("\n");//输出
mul(G, Q, m, n - m);//直接算余数R
for(int i = 0; i < m; i ++) R[i] = (F[i] - G[i] + mod) % mod;
for(int i = 0; i < m; i ++) printf("%lld ", R[i]); printf("\n");
return 0;
}