多项式全家桶
#include<bits/stdc++.h>
using namespace std;
const int mod=998244353,maxn=1<<22;
int f[maxn],g[maxn],q[maxn],r[maxn];
inline int add(int x,int y){if ((x+=y)>=mod) x-=mod;return x;}
inline int sub(int x,int y){if ((x-=y)<0) x+=mod;return x;}
int ksm(int x,int y){
int res=1;
while (y){
if (y&1) res=1ll*res*x%mod;
x=1ll*x*x%mod;
y>>=1;
}
return res;
}
namespace _NTT{
int rv[maxn];
void init(int len){
for (int i=1,t=len>>1;i<=len;++i) rv[i]=(rv[i>>1]>>1)|(i&1?t:0);
}
void NTT(int *f,int len,int rev){
for (int i=0;i<len;++i)
if (rv[i]<i)
swap(f[i],f[rv[i]]);
for (int l=2,m=1;l<=len;l<<=1,m<<=1){
int step=ksm(3,(mod-1)/l);
for (int i=0;i<len;i+=l)
for (int j=0,cur=1;j<m;++j,cur=1ll*cur*step%mod){
int tmp=1ll*cur*f[i+m+j]%mod;
f[i+m+j]=sub(f[i+j],tmp);
f[i+j]=add(f[i+j],tmp);
}
}
if (rev!=-1) return;
int inv=ksm(len,mod-2);
for (int i=0;i<len;++i) f[i]=1ll*f[i]*inv%mod;
reverse(f+1,f+len);
}
}
using _NTT::init;
using _NTT::NTT;
namespace _getinv{
int t1[maxn],t2[maxn];
void getinv(int *f,int len,int *g){
if (len==1){
g[0]=ksm(f[0],mod-2);
return;
}
int tlen=(len+1)>>1,klen=1;
getinv(f,tlen,g);
while (klen<2*len) klen<<=1;
for (int i=0;i<tlen;++i) t1[i]=g[i];
for (int i=tlen;i<klen;++i) t1[i]=0;
for (int i=0;i<len;++i) t2[i]=f[i];
for (int i=len;i<klen;++i) t2[i]=0;
init(klen);
NTT(t1,klen,1);
NTT(t2,klen,1);
for (int i=0;i<klen;++i) t1[i]=sub(add(t1[i],t1[i]),1ll*t1[i]*t1[i]%mod*t2[i]%mod);
NTT(t1,klen,-1);
for (int i=0;i<len;++i) g[i]=t1[i];
}
}
using _getinv::getinv;
namespace _getln{
int t1[maxn],t2[maxn],inv[maxn];
void getln(int *f,int len,int *g){
int klen=1;
while (klen<2*len) klen<<=1;
for (int i=0;i<len;++i) t2[i]=f[i];
getinv(t2,len,t1);
for (int i=len;i<klen;++i) t1[i]=0;
for (int i=0;i<len-1;++i) t2[i]=1ll*(i+1)*f[i+1]%mod;
for (int i=len-1;i<klen;++i) t2[i]=0;
init(klen);
NTT(t1,klen,1);
NTT(t2,klen,1);
for (int i=0;i<klen;++i) g[i]=1ll*t1[i]*t2[i]%mod;
NTT(g,klen,-1);
for (int i=len;i<klen;++i) g[i]=0;
inv[1]=1;
for (int i=2;i<len;++i) inv[i]=mod-1ll*(mod/i)*inv[mod%i]%mod;
for (int i=len-1;i>=1;--i) g[i]=1ll*inv[i]*g[i-1]%mod;
g[0]=0;
}
}
using _getln::getln;
namespace _getexp{
int t1[maxn],t2[maxn];
void getexp(int *f,int len,int *g){
if (len==1){
g[0]=1;
return;
}
int tlen=(len+1)>>1,klen=1;
while (klen<2*len) klen<<=1;
getexp(f,tlen,g);
for (int i=0;i<tlen;++i) t1[i]=g[i];
for (int i=tlen;i<klen;++i) t1[i]=0;
getln(t1,len,t2);
for (int i=0;i<len;++i) t2[i]=sub(f[i],t2[i]);
t2[0]=add(t2[0],1);
for (int i=len;i<klen;++i) t2[i]=0;
for (int i=0;i<tlen;++i) t1[i]=g[i];
for (int i=tlen;i<klen;++i) t1[i]=0;
init(klen);
NTT(t1,klen,1);
NTT(t2,klen,1);
for (int i=0;i<klen;++i) t1[i]=1ll*t1[i]*t2[i]%mod;
NTT(t1,klen,-1);
for (int i=0;i<len;++i) g[i]=t1[i];
}
}
using _getexp::getexp;
namespace _getsqrt{
const int inv2=ksm(2,mod-2);
int t1[maxn],t2[maxn];
void getsqrt(int *f,int len,int *g){
if (len==1){
g[0]=1;
return;
}
int tlen=(len+1)>>1,klen=1;
while (klen<2*len) klen<<=1;
getsqrt(f,tlen,g);
for (int i=0;i<tlen;++i) t1[i]=add(g[i],g[i]);
for (int i=tlen;i<klen;++i) t1[i]=0;
getinv(t1,len,t2);
for (int i=0;i<len;++i) t1[i]=f[i];
for (int i=len;i<klen;++i) t1[i]=0;
init(klen);
NTT(t1,klen,1);
NTT(t2,klen,1);
for (int i=0;i<klen;++i) t1[i]=1ll*t1[i]*t2[i]%mod;
NTT(t1,klen,-1);
for (int i=0;i<len;++i) g[i]=add(1ll*inv2*g[i]%mod,t1[i]);
}
}
using _getsqrt::getsqrt;
namespace _getksm{
int t1[maxn],t2[maxn];
char s[maxn];
void getksm(int *f,int len,int k,int *g){
if (!k){
g[0]=1;
return;
}
int A,I,i=0;
while (!f[i] && i<len) ++i;
if (i>len) return;
A=f[i],I=i;
int inv=ksm(A,mod-2),mi=ksm(A,k);
for (int i=I;i<len;++i) t2[i-I]=1ll*f[i]*inv%mod;
getln(t2,len-I,t1);
for (int i=0;i<len;++i) t1[i]=1ll*t1[i]*k%mod;
getexp(t1,len-I,t2);
for (int i=0;i<len-I;++i)
if (i+I*k<len) g[i+I*k]=1ll*mi*t2[i]%mod;
}
void read(int &k){
scanf("%s",s+1);k=0;
for (int i=1,len=strlen(s+1);i<=len;++i) k=(10ll*k%mod+s[i]-'0')%mod;
}
}
using _getksm::getksm;
namespace _getmod{
int t[maxn],t1[maxn],gr[maxn],fr[maxn];
void getmod(int *f,int n,int *g,int m,int *q,int *r){
int len=n-m+1,klen=1;
while (klen<2*len) klen<<=1;
for (int i=0;i<klen;++i) fr[i]=gr[i]=t1[i]=0;
for (int i=0;i<n;++i) fr[i]=f[n-i-1];
for (int i=0;i<m;++i) gr[i]=g[m-i-1];
getinv(gr,len,t);
for (int i=len;i<klen;++i) fr[i]=0;
init(klen);
NTT(fr,klen,1);
NTT(t,klen,1);
for (int i=0;i<klen;++i) fr[i]=1ll*fr[i]*t[i]%mod;
NTT(fr,klen,-1);
for (int i=0;i<len;++i) q[len-i-1]=fr[i];
for (int i=0;i<len;++i) t1[i]=q[i];
for (int i=0;i<m;++i) t[i]=g[i];
for (int i=m;i<klen;++i) t[i]=0;
klen=1;
while (klen<2*n) klen<<=1;
init(klen);
NTT(t,klen,1);
NTT(t1,klen,1);
for (int i=0;i<klen;++i) t[i]=1ll*t[i]*t1[i]%mod;
NTT(t,klen,-1);
for (int i=0;i<n;++i) r[i]=sub(f[i],1ll*t[i]);
}
}
using _getmod::getmod;
int n,m;
int main(){
freopen("P4512_4.in","r",stdin);
scanf("%d%d",&n,&m);++n;++m;
for (int i=0;i<n;++i) scanf("%d",&f[i]);
for (int i=0;i<m;++i) scanf("%d",&g[i]);
getmod(f,n,g,m,q,r);
for (int i=0;i<n-m+1;++i) printf("%d ",q[i]);
puts("");
for (int i=0;i<m-1;++i) printf("%d ",r[i]);
return 0;
}
FFT和NTT
首先最基本的FFT和NTT什么的应该能够默写了吧。
牛顿迭代法
假如有几个关于 A ( x ) A(x) A(x)的函数 g g g,现在要求解方程 g ( A ( x ) ) ≡ 0 m o d x n g(A(x))\equiv 0 \mod x^n g(A(x))≡0modxn,那么不妨先求出方程 g ( A 0 ( x ) ) ≡ 0 m o d x ⌈ n 2 ⌉ g(A_0(x))\equiv 0\mod x^{\lceil \frac{n}{2}\rceil} g(A0(x))≡0modx⌈2n⌉的解 A 0 ( x ) A_0(x) A0(x),那么 A ( x ) ≡ A 0 ( x ) − g ( A 0 ( x ) ) g ′ ( A 0 ( x ) ) m o d x n A(x)\equiv A_0(x)-\frac{g(A_0(x))}{g'(A_0(x))} \mod x^n A(x)≡A0(x)−g′(A0(x))g(A0(x))modxn。证明的话可以考虑在 A 0 ( x ) A_0(x) A0(x)处泰勒展开,其实已经忘了,但是似乎也不需要知道。
手握这个结论,可以很轻松的推式子。
多项式求逆
给 A ( x ) A(x) A(x)求 B ( x ) B(x) B(x)使得 B ( x ) A ( x ) ≡ 1 m o d x n B(x)A(x)\equiv 1 \mod x^n B(x)A(x)≡1modxn,当然也可以写作 B ( x ) ≡ A ( x ) − 1 m o d x n B(x)\equiv A(x)^{-1} \mod x^n B(x)≡A(x)−1modxn。
那么构造 g ( f ( x ) ) = f ( x ) − 1 − A ( x ) g(f(x))=f(x)^{-1}-A(x) g(f(x))=f(x)−1−A(x),套牛顿迭代可以得到 f ( x ) ≡ f 0 ( x ) − g ( f 0 ( x ) ) g ′ ( f 0 ( x ) ) f(x)\equiv f_0(x)-\frac{g(f_0(x))}{g'(f_0(x))} f(x)≡f0(x)−g′(f0(x))g(f0(x))。注意!这里的 g ′ ( f 0 ( x ) ) g'(f_0(x)) g′(f0(x))是关于 f 0 ( x ) f_0(x) f0(x)的导,所以 g ′ ( f 0 ( x ) ) = − f 0 ( x ) − 2 g'(f_0(x))=-f_0(x)^{-2} g′(f0(x))=−f0(x)−2。至于分子直接带入即可,那么整理一下就是 f ( x ) ≡ 2 f 0 ( x ) − f 0 ( x ) 2 A ( x ) m o d x n f(x)\equiv 2f_0(x)-f_0(x)^2A(x) \mod x^n f(x)≡2f0(x)−f0(x)2A(x)modxn,把上面的 f f f换成 B B B就可以迭代了。
多项式对数
给
A
(
x
)
A(x)
A(x)求
B
(
x
)
≡
ln
A
(
x
)
m
o
d
x
n
B(x) \equiv \ln A(x) \mod x^n
B(x)≡lnA(x)modxn,可以试一下牛顿迭代,可以发现构造的函数里面有
exp
B
(
x
)
\exp B(x)
expB(x),行不通,所以这里采取求导再积分的方式。
d
ln
A
(
x
)
d
x
≡
f
′
(
x
)
f
(
x
)
m
o
d
x
n
ln
f
(
x
)
≡
∫
d
ln
x
≡
∫
f
′
(
x
)
f
(
x
)
d
x
m
o
d
x
n
\begin{aligned} \frac{\mathrm{d} \ln{A(x)}}{\mathrm{dx}}&\equiv \frac{f'(x)}{f(x)}& \mod x^n\\ \ln{f(x)}&\equiv \int \mathrm{d} \ln{x}\equiv \int \frac{f'(x)}{f(x)} \mathrm{dx}& \mod x^n \end{aligned}
dxdlnA(x)lnf(x)≡f(x)f′(x)≡∫dlnx≡∫f(x)f′(x)dxmodxnmodxn
那么求导就是简单的位移,求逆上面有了,然后卷一次,最后积分回去即可。
注意,这个要求 [ x 0 ] A ( x ) = 1 [x^0]A(x) =1 [x0]A(x)=1
多项式指数
给 A ( x ) A(x) A(x)求 B ( x ) ≡ exp A ( x ) m o d x n B(x) \equiv \exp {A(x)} \mod x^n B(x)≡expA(x)modxn,这个可以牛顿迭代,直接构造 g ( f ( x ) ) = ln f ( x ) − A ( x ) g(f(x))=\ln f(x)-A(x) g(f(x))=lnf(x)−A(x),那么 f ( x ) = f 0 ( x ) ( 1 − ln f 0 ( x ) + A ( x ) ) m o d x n f(x)=f_0(x)(1-\ln f_0(x)+A(x)) \mod x^n f(x)=f0(x)(1−lnf0(x)+A(x))modxn,里面的那个 ln \ln ln直接用上面的就好了。
注意,这里要求 [ x 0 ] A ( x ) = 0 [x^0]A(x)=0 [x0]A(x)=0
多项式开根
给 A ( x ) A(x) A(x)求 B ( x ) B(x) B(x)使得 B ( x ) 2 ≡ A ( x ) m o d x n B(x)^2\equiv A(x) \mod x^n B(x)2≡A(x)modxn,还是牛顿迭代,可以得到 f ( x ) ≡ f 0 ( x ) 2 + A ( x ) 2 f 0 ( x ) m o d x n f(x)\equiv \frac{f_0(x)^2+A(x)}{2f_0(x)} \mod x^n f(x)≡2f0(x)f0(x)2+A(x)modxn。
多项式快速幂
给 A ( x ) A(x) A(x)求 B ( x ) ≡ A ( x ) k m o d x n B(x)\equiv A(x)^k \mod x^n B(x)≡A(x)kmodxn,那么可以简单的推一下式子就是 B ( x ) ≡ A ( x ) k ≡ exp ln A ( x ) k ≡ exp k ln A ( x ) m o d x n B(x)\equiv A(x)^k \equiv \exp \ln A(x)^k \equiv \exp k\ln A(x) \mod x^n B(x)≡A(x)k≡explnA(x)k≡expklnA(x)modxn,于是就可以求解了。
当然由于 ln \ln ln和 exp \exp exp对 [ x 0 ] A ( x ) [x^0]A(x) [x0]A(x)都是由要求的,所以可能要先位移一下。
多项式取模
给 A ( x ) , B ( x ) A(x),B(x) A(x),B(x)求 Q ( x ) , R ( x ) Q(x),R(x) Q(x),R(x),其中 A ( x ) ≡ Q ( x ) B ( x ) + R ( x ) m o d n , deg Q ( x ) = deg A ( x ) − deg B ( x ) , deg R ( x ) ≤ deg B ( x ) A(x)\equiv Q(x)B(x)+R(x) \mod n,\deg Q(x)=\deg A(x)-\deg B(x),\deg R(x)\le \deg B(x) A(x)≡Q(x)B(x)+R(x)modn,degQ(x)=degA(x)−degB(x),degR(x)≤degB(x)。
这个比较特别,思路不一样。
首先定义 f R ( x ) ≡ x n f ( 1 x ) m o d x n f_R(x)\equiv x^nf(\frac{1}{x}) \mod x^n fR(x)≡xnf(x1)modxn,就是保留 n n n项并反转。
那么开始推式子:
A
(
x
)
≡
Q
(
x
)
B
(
x
)
+
R
(
x
)
m
o
d
x
n
A
(
1
x
)
≡
Q
(
1
x
)
B
(
1
x
)
+
R
(
1
x
)
m
o
d
x
n
x
n
A
(
1
x
)
≡
x
n
−
m
Q
(
1
x
)
x
m
B
(
1
x
)
+
x
n
−
m
x
m
R
(
1
x
)
m
o
d
x
n
A
R
(
x
)
≡
Q
R
(
x
)
B
R
(
x
)
+
x
n
−
m
R
R
(
x
)
m
o
d
x
n
A
R
(
x
)
≡
Q
R
(
x
)
B
R
(
x
)
m
o
d
x
n
−
m
\begin{aligned} A(x) &\equiv Q(x)B(x)+R(x) &\mod x^n\\ A(\frac{1}{x}) & \equiv Q(\frac{1}{x})B(\frac{1}{x})+R(\frac{1}{x}) &\mod x^n\\ x^nA(\frac{1}{x}) & \equiv x^{n-m}Q(\frac{1}{x})x^mB(\frac{1}{x})+x^{n-m}x^{m}R(\frac{1}{x}) & \mod x^n\\ A_R(x) & \equiv Q_R(x)B_R(x)+x^{n-m}R_R(x) & \mod x^n\\ A_R(x) & \equiv Q_R(x)B_R(x) &\mod x^{n-m}\\ \end{aligned}
A(x)A(x1)xnA(x1)AR(x)AR(x)≡Q(x)B(x)+R(x)≡Q(x1)B(x1)+R(x1)≡xn−mQ(x1)xmB(x1)+xn−mxmR(x1)≡QR(x)BR(x)+xn−mRR(x)≡QR(x)BR(x)modxnmodxnmodxnmodxnmodxn−m
上面的式子可以直接
m
o
d
x
n
−
m
\mod x^{n-m}
modxn−m是因为
deg
Q
(
x
)
=
n
−
m
\deg Q(x)=n-m
degQ(x)=n−m,这样
Q
(
x
)
Q(x)
Q(x)就求出来了,然后带入可以求出
R
(
x
)
R(x)
R(x)。