NTT(快速数论变换)

大佬博客
和FFT一样都是快速求多项式乘法的,但是这个用的是整数,没有精度误差,运算起来也会更快。

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
using namespace std;
const int maxn=1<<18;
const int P=(479<<21)+1;//大素数
const int G=3;
const int sz=20;
int len;
LL wn[sz],a[maxn],b[maxn];
char s1[maxn],s2[maxn];
LL quick_mod(LL a,LL b,LL mod)
{
    LL res=1;
    a%=mod;
    while(b)
    {
        if(b&1)
            res=(res*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return res;
}
void get_wn()
{
    for(int i=0; i<sz; i++)
    {
        int t=1<<i;
        wn[i]=quick_mod(G,(P-1)/t,P);
    }
}
void init()
{
    len=1;
    int len1=strlen(s1),len2=strlen(s2);
    while(len<=len1*2||len<=len2*2) len<<=1;
    for(int i=0; i<len; i++)
    {
        if(i<len1) a[i]=(LL)s1[len1-i-1]-'0';
        else a[i]=0;
        if(i<len2) b[i]=(LL)s2[len2-i-1]-'0';
        else b[i]=0;
    }
}
void Rader(LL a[],int len)//雷德算法--倒位序
{
    int j=len>>1;
    for(int i=1; i<len-1; i++)
    {
        if(i<j) swap(a[i],a[j]);
        int k=len>>1;
        while(j>=k)
        {
            j-=k;
            k>>=1;
        }
        if(j<k) j+=k;
    }
}
void NTT(LL a[],int len,int on)//快速数论变换
{
    Rader(a,len);
    int id=0;
    for(int h=2; h<=len; h<<=1)
    {
        id++;
        for(int j=0; j<len; j+=h)
        {
            LL w=1;
            for(int k=j; k<j+h/2; k++)
            {
                LL u=a[k]%P;
                LL t=w*a[k+h/2]%P;
                a[k]=(u+t)%P;
                a[k+h/2]=(u-t+P)%P;
                w=w*wn[id]%P;
            }
        }
    }
    if(on==-1)
    {
        for(int i=1; i<len/2; i++)
            swap(a[i],a[len-i]);
        LL inv=quick_mod(len,P-2,P);
        for(int i=0; i<len; i++)
            a[i]=a[i]*inv%P;
    }
}
void Conv(LL a[],LL b[],int len)//求卷积
{
    NTT(a,len,1);
    NTT(b,len,1);
    for(int i=0; i<len; i++)
        a[i]=a[i]*b[i]%P;
    NTT(a,len,-1);
}
void solve(LL a[],int len)
{
    for(int i=0; i<len; i++)
    {
        a[i+1]+=a[i]/10;
        a[i]%=10;
    }
    int f=0;
    for(int i=len-1; i>=0; i--)
    {
        if(a[i])
        {
            for(int j=i; j>=0; j--)
                f=1,printf("%d",a[j]);
            break;
        }
    }
    if(!f) printf("0");
    printf("\n");
}
int main()
{
    get_wn();
    while(~scanf("%s%s",s1,s2))
    {
        init();
        Conv(a,b,len);
        solve(a,len);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值