.
最近出了几个这样的题于是就把模板库完善了一下
依然是全程NTT实现,模数为998244353,依然没有多点插值和多点求值
不过这次画风大变,因为结构体化的多项式模板其实无论用什么实现方式(数组,指针,vector)都不能同时兼顾效率和灵活性两方面,所以整个代码都是用数组实现
稍微写一下一些需要注意的点:
多项式求逆
大概是基于一个思想:倍增
首先当
d
e
g
(
A
)
=
1
deg(A)=1
deg(A)=1时我们有
B
=
A
−
1
=
A
M
−
2
B=A^{-1}=A^{M-2}
B=A−1=AM−2 注意常数项不能为0
下面考虑
d
e
g
(
A
)
>
1
deg(A)>1
deg(A)>1
假设我们知道了
A
(
x
)
∗
B
′
(
x
)
=
1
(
m
o
d
x
n
)
A(x)*B'(x)=1\ (mod\ x^n)
A(x)∗B′(x)=1 (mod xn)
现在我们要求的是
A
(
x
)
∗
B
(
x
)
=
1
(
m
o
d
x
2
n
)
A(x)*B(x)=1\ (mod\ x^{2n})
A(x)∗B(x)=1 (mod x2n)
首先必然有的就是
A
(
x
)
∗
(
B
′
(
x
)
−
B
(
x
)
)
=
0
(
m
o
d
x
n
)
A(x)*(B'(x)-B(x))=0\ (mod\ x^n)
A(x)∗(B′(x)−B(x))=0 (mod xn)
那么就有
B
′
(
x
)
−
B
(
x
)
=
0
(
m
o
d
x
n
)
B'(x)-B(x)=0\ (mod\ x^n)
B′(x)−B(x)=0 (mod xn)
两边平方就是
B
2
−
2
B
B
′
+
B
′
2
=
0
(
m
o
d
x
2
n
)
B^2-2BB'+B'^2=0\ (mod\ x^{2n})
B2−2BB′+B′2=0 (mod x2n)上面这一步非常关键
接下来用前面的结论
A
B
′
=
1
AB'=1
AB′=1就得到
B
−
2
B
′
+
A
B
′
2
=
0
(
m
o
d
x
2
n
)
B-2B'+AB'^2=0\ (mod\ x^{2n})
B−2B′+AB′2=0 (mod x2n)
那么我们就求得了
B
(
x
)
=
2
B
′
−
A
B
′
2
B(x)=2B'-AB'^2
B(x)=2B′−AB′2
写的时候直接把次数取到2的幂次会比较方便
多项式求Ln
一个非常好的问题
首先定义?就是 Ln(A)的泰勒展开而已
这个时候就会发现一些问题
如果A的常数项不为1那么我们发现
l
n
(
A
)
\ ln(A)
ln(A)是不能在模意义下表示的
所以我们这里只讨论常数项为1的情况(不为1请用FFT而不是NTT)
注意到以下的式子
d
(
l
n
(
A
)
)
=
d
A
A
d(ln(A))=\frac{dA}{A}
d(ln(A))=AdA
这给我们求ln提供了方法,先对A求导,乘上A的逆元让后积分就可以了
积分常数C一般取0
这个还是比较好理解的
多项式Exp
这个就不那么好解释了
先给一个牛顿迭代的式子
如果有式子
F
F
F满足
G
(
F
(
x
)
)
=
0
(
m
o
d
x
n
)
G(F(x))=0\ (mod\ x^n)
G(F(x))=0 (mod xn)
那么我们就得到
H
(
x
)
=
F
(
x
)
−
G
(
F
(
x
)
)
/
G
′
(
F
(
x
)
)
H(x)=F(x)-G(F(x))/G'(F(x))
H(x)=F(x)−G(F(x))/G′(F(x))这个H满足
G
(
H
(
x
)
)
=
0
(
m
o
d
x
2
n
)
G(H(x))=0\ (mod\ x^{2n})
G(H(x))=0 (mod x2n)
我们现在要求的是
l
n
(
F
(
x
)
)
−
A
(
x
)
=
0
(
m
o
d
x
n
)
ln(F(x))-A(x)=0\ (mod\ x^n)
ln(F(x))−A(x)=0 (mod xn)
设
G
(
F
(
x
)
)
=
l
n
(
F
(
x
)
)
−
A
(
x
)
G(F(x))=ln(F(x))-A(x)
G(F(x))=ln(F(x))−A(x)带入上面那个式子就得到
H
(
x
)
=
F
(
x
)
⋅
(
1
−
l
n
(
F
(
x
)
)
+
A
(
x
)
H(x)=F(x)·(1-ln(F(x))+A(x)
H(x)=F(x)⋅(1−ln(F(x))+A(x)
这样,套用上面求逆的倍增法就可以了
同样注意,只有当常数项为0的时候这个算法才能使用否则请用FFT
多项式开方
依然是沿用上面的倍增方法
我们考虑已经得到
B
′
2
=
A
(
m
o
d
x
n
)
B'^2=A\ (mod\ x^n)
B′2=A (mod xn)
那么就得到
(
B
+
B
′
)
∗
(
B
−
B
′
)
=
0
(
m
o
d
x
n
)
(B+B')*(B-B')=0\ (mod\ x^n)
(B+B′)∗(B−B′)=0 (mod xn)
取一个解就得到
B
−
B
′
=
0
(
m
o
d
x
n
)
B-B'=0\ (mod\ x^n)
B−B′=0 (mod xn)
平方一下就得到
B
2
−
2
B
B
′
+
B
′
2
=
0
(
m
o
d
x
2
n
)
B^2-2BB'+B'^2=0 \ (mod\ x^{2n})
B2−2BB′+B′2=0 (mod x2n)
又因为我们已知
B
2
=
A
B^2=A
B2=A
所以就有
B
(
x
)
=
A
(
x
)
+
B
′
2
(
x
)
2
B
′
(
x
)
B(x)=\frac{A(x)+B'^2(x)}{2B'(x)}
B(x)=2B′(x)A(x)+B′2(x)
套用上面的多项式求逆就可以了
除法这里暂时不写(代码里面有)
#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 400010
#define M 998244353
#define LL long long
using namespace std;
int W[N],iW[N],inv[N];
int rA[N],rB[N],C[N],b[N],iA[N],iB[N],dA[N];
inline LL pow(LL x,LL k,LL s=1){
for(;k;x=x*x%M,k>>=1) k&1?s=s*x%M:0;
return s;
}
inline void init(){
inv[1]=1;
for(int j=2;j<N;++j)
inv[j]=(LL)(M-M/j)*inv[M%j]%M;
for(int j=1;j<=N;j<<=1){
W[j]=pow(3,(M-1)/j);
iW[j]=pow(W[j],M-2);
}
}
inline void NTT(int* A,int n,int g){
static int b[N]; memcpy(b,A,n<<2);
for(int i=0,j=0;i<n;i++){
if(i>j) swap(b[i],b[j]);
for(int l=n>>1;(j^=l)<l;l>>=1);
}
for(int k=1;k<n;k<<=1){
register LL w=g?W[k<<1]:iW[k<<1],u,v,z=1;
for(int j=k;j<n;++j&k?z=z*w%M:z=1,j|=k){
u=b[j^k]; v=z*b[j]%M;
b[j^k]=(u+v)%M; b[j]=(u-v+M)%M;
}
}
if(!g){
g=inv[n];
for(int i=0;i<n;++i) A[i]=(LL)b[i]*g%M;
} else memcpy(A,b,n<<2);
}
inline void gInv(int* A,int* B,int n){
if(n==1){ *B=pow(*A,M-2); return; }
gInv(A,B,n>>1);
for(int i=0;i<n;++i) C[i]=A[i],C[i+n]=0;
NTT(C,n<<1,1);
NTT(B,n<<1,1);
for(int i=0;i<(n<<1);++i) C[i]=B[i]*(M+2-(LL)C[i]*B[i]%M)%M;
NTT(C,n<<1,0);
for(int i=0;i<n;++i) B[i]=C[i],B[i+n]=0;
}
inline void Sqrt(int* A,int* B,int n){
if(n==1){ *B=*A; return; }
Sqrt(A,B,n>>1);
for(int i=0;i<n;++i) C[i]=A[i],iB[i]=iB[i+n]=C[i+n]=0;
gInv(B,iB,n);
NTT(C,n<<1,1);
NTT(iB,n<<1,1);
for(int i=0;i<(n<<1);++i) C[i]=(LL)C[i]*inv[2]%M*iB[i]%M;
NTT(C,n<<1,0);
for(int i=0;i<n;++i) B[i]=((LL)B[i]*inv[2]%M+C[i])%M;
}
inline void Div(int* A,int* B,int n,int m,int* D,int* R){
for(int i=0;i<m;++i) rB[i]=B[m-i-1];
for(int i=0;i<n;++i) rA[i]=A[n-i-1];
int dG=n-m+1,L=1; for(;L<(dG<<1);L<<=1);
for(int i=dG;i<L;++i) rA[i]=rB[i]=0;
for(int i=0;i<L;++i) iB[i]=0;
gInv(rB,iB,L);
for(int i=dG;i<L;++i) iB[i]=0;
NTT(iB,L,1);
NTT(rA,L,1);
for(int i=0;i<L;++i) rA[i]=(LL)rA[i]*iB[i]%M;
NTT(rA,L,0);
for(int i=0;i<dG;++i) D[i]=rA[dG-i-1];
for(int i=0;i<dG;++i) rA[i]=D[i];
for(int i=0;i<m;++i) rB[i]=B[i];
for(;L<(m+dG);L<<=1);
for(int i=dG;i<L;++i) rA[i]=0;
for(int i=m;i<L;++i) rB[i]=0;
NTT(rA,L,1);
NTT(rB,L,1);
for(int i=0;i<L;++i) rA[i]=(LL)rA[i]*rB[i]%M;
NTT(rA,L,0);
for(int i=0;i<L;++i) R[i]=(A[i]-rA[i]+M)%M;
}
inline void Ln(int* A,int* B,int n){
for(int i=0;i<(n<<1);++i) iA[i]=dA[i]=0;
gInv(A,iA,n);
for(int i=1;i<n;++i) dA[i-1]=(LL)A[i]*i%M;
NTT(dA,n<<1,1);
NTT(iA,n<<1,1);
for(int i=0;i<(n<<1);++i) dA[i]=(LL)iA[i]*dA[i]%M;
NTT(dA,n<<1,0);
*B=B[n]=0;
for(int i=1;i<n;++i) B[i]=(LL)dA[i-1]*inv[i]%M,B[i+n]=0;
}
inline void Exp(int* A,int* B,int n){
if(n==1){ *B=1; return; }
Exp(A,B,n>>1);
for(int i=0;i<(n<<1);++i) b[i]=0;
Ln(B,b,n);
for(int i=0;i<n;++i) b[i]=((LL)A[i]-b[i]+M)%M;
*b=(*b+1)%M;
NTT(B,n<<1,1);
NTT(b,n<<1,1);
for(int i=0;i<(n<<1);++i) B[i]=(LL)B[i]*b[i]%M;
NTT(B,n<<1,0);
}
int F[N],G[N],n,H[N],m,k;
int main(){
// freopen("1.in","r",stdin);
init(); scanf("%d%d",&n,&k); ++n;
for(int i=0;i<n;++i) scanf("%d",F+i);
for(m=1;m<n;m<<=1);
Ln(F,G,m);
for(int i=0;i<n;++i) G[i]=(LL)G[i]*k%M;
memset(F,0,sizeof F);
Exp(G,F,m);
for(int i=0;i<n;++i) printf("%d ",F[i]);
}