检查数据:
input:
6
1 2 3 4 5 6
1.NTT多项式求逆
#include<bits/stdc++.h>
#define maxn 2100005
#define LL long long
#define mod 998244353
using namespace std;
int lg[maxn],r[maxn],w[maxn]={1},wlen;
int Pow(int base,int k)
{ int ret=1;
for(;k;k>>=1,base=1ll*base*base%mod) if(k&1) ret=1ll*ret*base%mod;
return ret;}
inline void NTT(int A[maxn],int n,int tp)//n=2^k
{
int lgn = lg[n];
for(int i=1;i<n;i++) r[i] = (r[i>>1]>>1)|((i&1)<<(lgn-1));
for(int i=1;i<n;i++) if(i<r[i]) swap(A[i],A[r[i]]);
for(int L=2;L<=n;L<<=1)
for(int st=0,l=L>>1,inc=wlen/l;st<n;st+=L)
for(int k=st,x=0;k<st+l;k++,x+=inc)
{ int tmp = 1ll *(tp==1?w[x]:w[2*wlen-x])*A[k+l] % mod;
A[k+l]=(A[k]-tmp)%mod,A[k]=(A[k]+tmp)%mod;}
if(tp==-1) for(int i=0,inv=Pow(n,mod-2);i<n;i++) A[i]=1ll*A[i]*inv%mod;
}
void Inv(int A[maxn],int B[maxn],int n)
{ B[0] = Pow(A[0],mod-2);
static int tmp[maxn];
for(int i=2;i<(n<<1);i<<=1)// mod x^i
{ for(int j=0;j<i;j++) tmp[j] = j<n?A[j]:0;
NTT(tmp,i<<1,1),NTT(B,i<<1,1);
for(int j=0;j<(i<<1);j++) B[j]=1ll*B[j]*(2-1ll*B[j]*tmp[j]%mod)%mod;
NTT(B,i<<1,-1); for(int j=min(n,i);j<(i<<1);j++) B[j] = 0;}
}
int a[maxn],b[maxn];
int main()
{
int n;
scanf("%d",&n);
for(wlen=1;(n<<1)>(wlen<<1);wlen<<=1);
for(int i=1,pw=Pow(3,(mod-1)/(2*wlen));i<=2*wlen;i++) w[i] = 1ll * w[i-1] * pw % mod;
for(int i=2;i<=2*wlen;i++) lg[i] = lg[i>>1]+1;
for(int i=0;i<n;i++) scanf("%d",&a[i]);
Inv(a,b,n);
for(int i=0;i<n;i++) printf("%d%c",(b[i]+mod)%mod,i==n-1?'\n':' ');
}
output:
2.多项式除法:
感觉我好短啊。
#include<bits/stdc++.h>
#define maxn 2100005
#define LL long long
#define mod 998244353
using namespace std;
int lg[maxn],r[maxn],w[maxn]={1},wlen;
int Pow(int base,int k)
{ int ret=1;
for(;k;k>>=1,base=1ll*base*base%mod) if(k&1) ret=1ll*ret*base%mod;
return ret;}
inline void NTT(int A[maxn],int n,int tp)//n=2^k
{ int lgn = lg[n];
for(int i=1;i<n;i++) r[i] = (r[i>>1]>>1)|((i&1)<<(lgn-1));
for(int i=1;i<n;i++) if(i<r[i]) swap(A[i],A[r[i]]);
for(int L=2;L<=n;L<<=1)
for(int st=0,l=L>>1,inc=wlen/l;st<n;st+=L)
for(int k=st,x=0;k<st+l;k++,x+=inc)
{ int tmp = 1ll *(tp==1?w[x]:w[2*wlen-x])*A[k+l] % mod;
A[k+l]=(A[k]-tmp)%mod,A[k]=(A[k]+tmp)%mod;}
if(tp==-1) for(int i=0,inv=Pow(n,mod-2);i<n;i++) A[i]=1ll*A[i]*inv%mod;}
void mul(int A[maxn],int B[maxn],int C[maxn],int n,int m)
{ int lgn = lg[n+m]+1,len = 1<<lgn;
static int sta[maxn];
for(int i=0;i<len;i++) sta[i]=i<=n?A[i]:0,C[i]=i<=m?B[i]:0;
NTT(sta,len,1),NTT(C,len,1);
for(int i=0;i<len;i++) C[i] = 1ll * C[i] * sta[i] % mod;
NTT(C,len,-1);}
void Inv(int A[maxn],int B[maxn],int n)
{ B[0] = Pow(A[0],mod-2);
static int tmp[maxn];
for(int i=2;i<(n<<1);i<<=1)// mod x^i
{ for(int j=0;j<i;j++) tmp[j] = j<n?A[j]:0;
NTT(tmp,i<<1,1),NTT(B,i<<1,1);
for(int j=0;j<(i<<1);j++) B[j]=1ll*B[j]*(2-1ll*B[j]*tmp[j]%mod)%mod;
NTT(B,i<<1,-1); for(int j=min(n,i);j<(i<<1);j++) B[j] = 0;}}
void Div(int A[maxn],int B[maxn],int C[maxn],int R[maxn],int n,int m)
{ static int ra[maxn],rb[maxn];
for(int i=0,lim=max(n,m);i<=lim;i++) ra[i]=i<=n?A[n-i]:0,rb[i]=i<=m?B[m-i]:0;
Inv(rb,C,n-m+1),mul(ra,C,C,n,n-m);
for(int i=0;i<=2*n-m;i++) if(i<n-m-i)swap(C[i],C[n-m-i]);else if(i>n-m)C[i]=0;
mul(C,B,R,n-m,m);for(int i=0;i<=n;i++) R[i]=i<m?(A[i]-R[i])%mod:0;}
void Init(int n)
{ for(wlen=1;n>=(wlen<<1);wlen<<=1);
for(int i=1,pw=Pow(3,(mod-1)/(2*wlen));i<=2*wlen;i++) w[i]=1ll*w[i-1]*pw%mod;
for(int i=2;i<=2*wlen;i++) lg[i] = lg[i>>1] + 1;}
int a[maxn],b[maxn],c[maxn],R[maxn];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=0;i<=n;i++) scanf("%d",&a[i]);
for(int i=0;i<=m;i++) scanf("%d",&b[i]);
Init(n<<1);
Div(a,b,c,R,n,m);
for(int i=0;i<=n-m;i++) printf("%d%c",(c[i]+mod)%mod,i==n-m?'\n':' ');
for(int i=0;i<m;i++) printf("%d%c",(R[i]+mod)%mod,i==m-1?'\n':' ');
}
3.多项式exp
AC Code:(PS : 一旦到了需要多次调用函数的时候很多细节(清零)问题就出来了,以上的代码好像都没有考虑的说。。。。。。求逆,求
L
n
Ln
Ln,求
e
x
p
exp
exp都以本代码为准)
63行还是有点短的说。
#include<bits/stdc++.h>
#define maxn 300005
#define mod 998244353
using namespace std;
int lg[maxn],r[maxn],w[maxn]={1},wlen,inv[maxn]={1,1};
int Pow(int base,int k)
{ int ret = 1;
for(;k;k>>=1,base=1ll*base*base%mod) if(k&1) ret=1ll*ret*base%mod;
return ret; }
void Init(int n)
{ for(wlen=1;n>=(wlen*2);wlen<<=1);
for(int i=1,pw=Pow(3,(mod-1)/(2*wlen));i<=2*wlen;i++) w[i] = 1ll * w[i-1] * pw % mod;
for(int i=2;i<=2*wlen;i++) lg[i] = lg[i>>1] + 1;
for(int i=2;i<=2*wlen;i++) inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod; }
inline void NTT(int *A,int n,int tp)
{ int lgn = lg[n];
for(int i=1;i<n;i++) r[i] = (r[i>>1]>>1)|((i&1)<<(lgn-1));
for(int i=1;i<n;i++) if(i<r[i]) swap(A[i],A[r[i]]);
for(int L=2;L<=n;L<<=1)
for(int st=0,l=L>>1,inc=wlen/l;st<n;st+=L)
for(int k=st,x=0;k<st+l;k++,x+=inc)
{ int tmp = 1ll * (tp==1?w[x]:w[2*wlen-x]) * A[k+l] % mod;
A[k+l]=(A[k]-tmp)%mod,A[k]=(A[k]+tmp)%mod;}
if(tp==-1) for(int i=0,inv=Pow(n,mod-2);i<n;i++) A[i]=1ll*A[i]*inv%mod;}
void Inv(int *A,int *B,int n)
{ B[0] = Pow(A[0] , mod-2);
static int tmp[maxn];
for(int k=2;k<(n<<1);k<<=1)
{ for(int i=0,lim=min(k,n);i<(k<<1);i++) tmp[i]=i<lim?A[i]:0,B[i]=i<lim?B[i]:0;
NTT(tmp,k<<1,1),NTT(B,k<<1,1);
for(int i=0,lim=k<<1;i<lim;i++) B[i]=1ll*B[i]*(2-1ll*B[i]*tmp[i]%mod)%mod;
NTT(B,k<<1,-1);for(int i=min(k,n);i<(k<<1);i++) B[i] = 0;}
}
void cLn(int *A,int *B,int n)
{ Inv(A,B,n);
static int tmp[maxn];
int lgn = lg[n-1]+2 , len = 1<<lgn;
for(int i=0;i<len;i++) tmp[i]=i<n-1?1ll*A[i+1]*(i+1)%mod:0;
NTT(tmp,len,1),NTT(B,len,1);
for(int i=0;i<len;i++) tmp[i]=1ll*B[i]*tmp[i]%mod;
NTT(tmp,len,-1);B[0]=0;
for(int i=1;i<n;i++) B[i] = 1ll * tmp[i-1] * inv[i] % mod;
for(int i=n;i<len;i++) B[i] = 0;}
void eXp(int *A,int *B,int n)
{ B[0] = 1;
static int tmp[maxn];
for(int k=2;k<(n<<1);k<<=1)
{ cLn(B,tmp,k);
for(int i=0,lim=min(n,k);i<(k<<1);i++) tmp[i]=i<lim?((i==0)-tmp[i]+A[i])%mod:0,B[i]=i<lim?B[i]:0;
NTT(B,k<<1,1),NTT(tmp,k<<1,1);
for(int i=0;i<(k<<1);i++) B[i]=1ll*B[i]*tmp[i]%mod;
NTT(B,k<<1,-1);for(int i=min(n,k);i<(k<<1);i++) B[i]=0;}}
int a[maxn],b[maxn];
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%d",&a[i]);
Init(n<<1);
eXp(a,b,n);
for(int i=0;i<n;i++) printf("%d%c",(b[i]+mod)%mod,i==n-1?'\n':' ');
}
4多项式开根
#include<bits/stdc++.h>
#define maxn 300005
#define mod 998244353
using namespace std;
int w[maxn]={1},lg[maxn],r[maxn],wlen;
int Pow(int base,int k)
{ int ret = 1;
for(;k;k>>=1,base=1ll*base*base%mod) if(k&1) ret=1ll*ret*base%mod;
return ret;}
void Init(int n)
{ for(wlen=1;n>=2*wlen;wlen<<=1);
for(int i=1,pw=Pow(3,(mod-1)/(2*wlen));i<=2*wlen;i++) w[i]=1ll*w[i-1]*pw%mod;
for(int i=2;i<=2*wlen;i++) lg[i]=lg[i>>1]+1;}
inline void NTT(int *A,int n,int tp)
{ int lgn = lg[n];
for(int i=1;i<n;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(lgn-1));
for(int i=1;i<n;i++) if(i<r[i]) swap(A[i],A[r[i]]);
for(int L=2;L<=n;L<<=1)
for(int st=0,l=L>>1,inc=wlen/l;st<n;st+=L)
for(int k=st,x=0,tmp;k<st+l;k++,x+=inc)
tmp=1ll*(tp==1?w[x]:w[2*wlen-x])*A[k+l]%mod,
A[k+l]=(A[k]-tmp)%mod,A[k]=(A[k]+tmp)%mod;
if(tp==-1) for(int i=0,inv=Pow(n,mod-2);i<n;i++) A[i]=1ll*A[i]*inv%mod;}
void Inv(int *A,int *B,int n)
{ B[0]=Pow(A[0],mod-2),B[1]=B[2]=B[3]=0;
static int tmp[maxn];
for(int k=2;k<(n<<1);k<<=1)
{ for(int i=0;i<(k<<1);i++) tmp[i]=i<k?A[i]:0,B[i]=i<k?B[i]:0;
NTT(tmp,k<<1,1),NTT(B,k<<1,1);
for(int i=0;i<(k<<1);i++) B[i]=1ll*B[i]*(2-1ll*B[i]*tmp[i]%mod)%mod;
NTT(B,k<<1,-1);for(int i=min(k,n);i<(k<<1);i++) B[i]=0;}
}
void Sqt(int *A,int *B,int n)
{ B[0]=1,B[1]=B[2]=B[3]=0;
static int tmp[2][maxn];
for(int k=2;k<(n<<1);k<<=1)
{ for(int i=0;i<(k<<1);i++) tmp[0][i]=i<k?A[i]:0,B[i]=i<k?B[i]:0;
Inv(B,tmp[1],k),NTT(tmp[0],k<<1,1),NTT(tmp[1],k<<1,1),NTT(B,k<<1,1);
for(int i=0;i<(k<<1);i++) B[i] = 1ll * (mod+1)/2 * tmp[1][i] % mod * (tmp[0][i]+1ll*B[i]*B[i]%mod) % mod;
NTT(B,k<<1,-1);for(int i=k;i<(k<<1);i++) B[i]=0;}}
int n,m;
int a[maxn],b[maxn],c[maxn],d[maxn];
int main()
{
scanf("%d%d",&n,&m);m++;
for(int i=0,x;i<n;i++) scanf("%d",&x),a[x]++;
Init(m<<1);
for(int i=0;i<=m;i++) b[i] = (i==0) - 4ll * a[i];
Sqt(b,c,m);
c[0]++;
Inv(c,d,m);
for(int i=1;i<m;i++)
printf("%d\n",(2ll*d[i]%mod+mod)%mod);
}
5.多项式求幂
有3种算法:(后两种只能玩整数幂)
1.多项式exp
O
(
n
log
n
)
∗
30
O(n \log n) * 30
O(nlogn)∗30
2.多项式快速幂,每层3次FFT
T
(
n
)
=
T
(
n
/
2
)
+
O
(
l
e
n
log
l
e
n
)
,
实
际
是
O
(
l
e
n
log
l
e
n
log
n
)
T(n) = T(n/2) + O(len \log len),实际是O(len \log len \log n)
T(n)=T(n/2)+O(lenloglen),实际是O(lenloglenlogn)
3.多项式快速幂,NTT后对每个数单独进行快速幂然后再NTT回来。
O
(
n
log
n
)
O(n \log n)
O(nlogn)
关于这3者的区别可以看这篇博客
Codeforces 1096G Lucky Tickets