快速数论变换NTT

单位根

  多项式 $x^n=1$ 的根称为 n 次单位根。

  设 $\omega_{n}=e^{i\pi}$为复数,

  由欧拉公式$e^{ix}=cosx+isinx$

  容易验证$\omega_{n}^{n}=1$;

  

  考虑膜意义下的单位根  $x^{n}\equiv 1(mod\ p)$

  对于质数 $p=kn+1$,设 为$\mathbb{F}_{p}$中的元素,其中$g$为模$p$的原根,那么显然$\omega_{n}^{n}=1$。 

原根

原根,是一个数学符号。设$p$是正整数,$g$是整数,若$g$模$p$的阶等于$φ(p)$,则称$g$为模$p$的一个原根。

  那么对质数$p$而言,$\phi(p)=p-1$

设$a$,$p$是整数,$a$和$p$互素,那么:

使$a^{n}\equiv 1(mod\ p)$

成立的最小正整数$n$叫做$a$膜$p$的阶

  在上述两种情况下,1,$\omega_{n}^{1}$,$\omega_{n}^{2}$...$\omega_{n}^{n-1}$为$n$个不同的$n$次单位根

  $FTT$与$NTT$只在单位根定义上有些许差异,其他都大同小异

  由于要快速变换,所以要求$p=k*2^{l}+1$

  这样的话原根$g^{k*2^{l}}\equiv 1(mod\ p)$,

  而如果$g^{k*2^{l}}$不是2的整数倍的话,在分治\迭代过程中会出现分数指数,即会出现根号。

  这样可以处理$lim\leq 2^{l}$的多项式

  推荐模数:

模数 $k$ $l$ $g$
$998244353$ $7$$*$$17$ $23$ $3$

  

 

  注意:$2^{23}<10^{6}$

code:

  

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1<<22;
const int P=998244353;
const ll g=3;
const ll invg=332748118;
ll r[N],lim,L,n,m;
ll a[N],b[N];
ll ksm(ll x,ll n) {
    ll ans=1;
    while(n){
        if(n&1)ans=(ans*x)%P;
        n>>=1;
        x=(x*x)%P;
    }
    return ans;
}
void ntt(ll *f,int op){
    for(int i=0;i<lim;i++){
        if(i<r[i]){
            swap(f[i],f[r[i]]);
        }
    }
    for(int p=2;p<=lim;p<<=1){
        int len=p>>1;
        ll Wn=ksm(op==1?g:invg,(P-1)/p);
        for(int j=0;j<lim;j+=p){
            ll w=1;
            for(int k=j;k<j+len;k++){
                ll t=w*f[k+len]%P;
                f[k+len]=(f[k]-t+P)%P;
                f[k]=(f[k]+t)%P;
                w=(w*Wn)%P;
            }
        }
    }
}
int main(){
    cin>>n>>m;
    for(int i=0;i<=n;i++){
        scanf("%d",&a[i]);
        a[i]=(a[i]+P)%P;
    }
    for(int i=0;i<=m;i++){
        scanf("%d",&b[i]);
        b[i]=(b[i]+P)%P;
    }
    while(1<<L<=n+m)L+=1;
    lim=1<<L;
    for(int i=0;i<lim;i++){
        r[i]=(r[i>>1]>>1)|((i&1)<<(L-1));
    }
    ntt(a,1);
    ntt(b,1);
    for(int i=0;i<lim;i++){
        a[i]=a[i]*b[i]%P;
    }
    ntt(a,-1);
    ll inv=ksm(lim,P-2);
    for(int i=0;i<=n+m;i++){
        printf("%lld ",(a[i]*inv)%P);
    }
    puts("");
    return 0;
}

 

 

posted @ 2019-07-25 19:50  天才美少女雪乃  阅读(361)  评论(1编辑  收藏  举报