• NTT学习笔记


    前言

    在看本文之前,需要确保您已经会了FFT(总结在此)并且对于数学的掌握十分的好!

    优化

    我们考虑一下为什么FFT特别的慢,但是怎么优化呢?

    FFT慢的原因是:用了许多的实数导致运算复杂,而且容易出现精度问题,所以怎么解决?

    发现一个神奇的东西:

    ((W_n)^n=1,(W_n)^{n/2}=-1)

    有了这个不就是和原根很相同吗?

    下面有高能预警:
    引用yyb的话语:(主要是我自己说不清楚qwq)

    假设p的原根是g
    再膜p意义下:
    (g^{phi(p)}=1->g^{phi(p)/2}=sqrt{1})
    因为原根不存在一个比(phi(p))小的数使得(g^k=1)
    所以(g^{phi(p)/2}=−1)
    我们发现上面的性质也可以满足
    所以,把n次单位复根可以替换成原根的(phi(p)/(2^n))来做
    这样就解决了小数精度的问题


    但是非常遗憾的是NTT没有什么需要补充的,所以到此结束了qwq.


    代码

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<math.h>
    #include<algorithm>
    #include<queue>
    #include<iostream>
    using namespace std;
    #define ll long long
    #define file(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout)
    inline int gi(){
        int sum=0,f=1;char ch=getchar();
        while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0' && ch<='9'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
        return f*sum;
    }
    const int P=998244353,G=3,N=5000010;
    int f[N],g[N],limit,r[N],n,m;
    int Pow(int a,int b){
        int  ret=1;
        while(b){
            if(b&1)ret=(1ll*ret*a)%P;
            a=(1ll*a*a)%P;b>>=1;
        }
        return ret%P;
    }
    void ntt(int*A,int flag){
        for(int i=0;i<limit;i++)if(i<r[i])swap(A[i],A[r[i]]);
        for(int mid=1;mid<limit;mid<<=1){
            int root=Pow(G,(P-1)/(mid<<1));
            for(int R=mid<<1,j=0;j<limit;j+=R){
                int w=1;
                for(int k=0;k<mid;k++,w=1ll*w*root%P){
                    int x=A[j+k],y=1ll*w*A[mid+j+k]%P;
                    A[j+k]=(x+y)%P;A[mid+j+k]=(x-y+P)%P;
                }
            }
        }
        if(flag==-1)reverse(&A[1],&A[limit]);
    }
    int main(){
        n=gi();m=gi();
        for(int i=0;i<=n;i++)f[i]=gi();
        for(int i=0;i<=m;i++)g[i]=gi();
        limit=1;int l=0;
        while(limit<=(n+m))limit<<=1,l++;
        for(int i=0;i<limit;i++)
            r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
        ntt(f,1);ntt(g,1);
        for(int i=0;i<limit;i++)f[i]=1ll*f[i]*g[i]%P;
        ntt(f,-1);
        int inv=Pow(limit,P-2);
        for(int i=0;i<limit;i++)f[i]=1ll*f[i]*inv%P;
        for(int i=0;i<=n+m;i++)printf("%d ",f[i]);
        puts("");
        return 0;
    }
    
  • 相关阅读:
    socket 网络编程
    错误与异常
    正则与计算器
    正则爬虫案例
    面向对象
    模块与包
    常用模块-----configparser & subprocess
    正则表达式&re模块
    常用模块---sys&logging&序列化模块(json&pickle)
    常用模块----time&random&hushlib&os
  • 原文地址:https://www.cnblogs.com/mleautomaton/p/10309720.html
Copyright © 2020-2023  润新知