• 高速数论变换(NTT)


    今天的A题。裸的ntt,但我不会,于是白送了50分。


    于是跑来学一下ntt。
    题面非常easy。就懒得贴了,那不是我要说的重点。


    重点是NTT,也称高速数论变换。
    在非常多问题中,我们可能会遇到在模意义下的多项式乘法问题,这时传统的高速傅里叶变换可能就无法满足要求,这时候高速数论变换就派上了用场。
    考虑高速傅里叶变换的实现,利用单位复根的特殊性质来降低运算。而利用的。就是dft变换的循环卷积特性。

    于是考虑在模意义下相同具有循环卷积特性的东西。
    考虑在模p意义下(p为特定的质数,满足p=c2n+1)
    我们令p的一个原根为g,于是类比fft,我们的单位根为gp1n,然后其他的处理都类比fft。
    UPD:这是uoj34的代码

    #include <bits/stdc++.h>
    
    using namespace std;
    
    typedef long long ll;
    typedef double db;
    
    const int inf=0x3f3f3f3f;
    
    int getint()
    {
        int f=1,g=0;char c=getchar();
        while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0' && c<='9')g=(g<<3)+(g<<1)+c-'0',c=getchar();
        return f*g;
    }
    
    const int maxn=300005;
    const int mod=998244353;
    
    const int G=3;
    
    int a[maxn];
    int b[maxn];
    int c[maxn];
    int n,m;
    int rev[maxn];
    int N;
    int len;
    int inv;
    
    int power(ll x,ll y)
    {
        ll res=1ll;
        for(;y;y>>=1,x=(x*x)%mod)
        {
            if(y&1)res=(res*x)%mod;
        }
        return res;
    }
    
    void init()
    {
        while((n+m)>=(1<<len))len++;
        N=(1<<len);
        inv=power(N,mod-2);
        for(int i=0;i<N;i++)
        {
            int pos=0;
            int temp=i;
            for(int j=1;j<=len;j++)
            {
                pos<<=1;pos |= temp&1;temp>>=1;
            }
            rev[i]=pos;
        }
    }
    
    void ntt(int *a,int n,int re)
    {
        for(int i=0;i<n;i++)
        {
            if(rev[i]>i)
            {
                swap(a[i],a[rev[i]]);
            }
        }
        for(int i=2;i<=n;i<<=1)
        {
            int mid=i>>1;
    
            int wn=power(G,(mod-1)/i);
            if(re) wn=power(wn,(mod-2));
            for(int j=0;j<n;j+=i)
            {
                int w=1;
                for(int k=0;k<mid;k++)
                {
                    int temp1=a[j+k];
                    int temp2=(ll)a[j+k+mid]*w%mod;
                    a[j+k]=(temp1+temp2);if(a[j+k]>=mod)a[j+k]-=mod;
                    a[j+k+mid]=(temp1-temp2);if(a[j+k+mid]<0)a[j+k+mid]+=mod;
                    w=(ll)w*wn%mod;
                }
            }
        }
        if(re)
        {
            for(int i=0;i<n;i++)
            {
                a[i]=(ll)a[i]*inv%mod;
            }
        }
    }
    
    int main()
    {
        n=getint();
        m=getint();
    
        for(int i=0;i<=n;i++)
        {
            a[i]=getint();
        }
        for(int i=0;i<=m;i++)
        {
            b[i]=getint();      
        }
    
        init();
    
        ntt(a,N,0);
        ntt(b,N,0);
        for(int i=0;i<=N;i++)
        {
            c[i]=(ll)a[i]*b[i]%mod;
        }
        ntt(c,N,1);
        for(int i=0;i<=n+m;i++)
        {
            printf("%d%c",c[i]," 
    "[i==n+m]);
        }
    
        return 0;
    }
    
  • 相关阅读:
    基于 HTML5 + WebGL 的 3D 风力发电场
    基于HTML5 WebGL的工业化3D电子围栏
    基于 HTML5 WebGL 和 VR 技术的 3D 机房数据中心可视化
    基于 HTML5 Canvas 的 Web SCADA 组态电机控制面板
    基于 HTML5 WebGL 与 WebVR 3D 虚拟现实的可视化培训系统
    基于 HTML5 WebGL 的 3D 服务器与客户端的通信
    什么是 SUID, SGID 和 Sticky bit
    贝塞尔曲线切割圆角
    iOS-获取当前View所在的控制器
    block(八)作用域
  • 原文地址:https://www.cnblogs.com/yutingliuyl/p/7018798.html
Copyright © 2020-2023  润新知