FFT/NTT模板 51nod1028 大数乘法 V2

题目链接
FFT:
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define maxn 400005
using namespace std;
const int wei = 3, bit = 1e3;//压三位
const double Pi = acos(-1);
struct complex
{
	double r,i;
	complex(double _r=0,double _i=0):r(_r),i(_i){}
	complex operator + (const complex &t)const{return complex(r+t.r,i+t.i);}
	complex operator - (const complex &t)const{return complex(r-t.r,i-t.i);}
	complex operator * (const complex &t)const{return complex(r*t.r-i*t.i,r*t.i+i*t.r);}
}a1[maxn],a2[maxn],w,wn;
void change(complex *a,int len)
{
	for(int i=1,j=len/2,k;i<len-1;i++)
	{
		if(i<j) swap(a[i],a[j]);
		for(k=len/2;j>=k;j-=k,k>>=1);
		j+=k;
	}
}
void fft(complex *a,int len,int flg)
{
	for(int i=2;i<=len;i<<=1)
	{
		wn=complex(cos(2*flg*Pi/i),sin(2*flg*Pi/i));
		for(int j=0;j<len;j+=i)
		{
			w=complex(1,0);
			for(int k=j;k<j+i/2;k++)
			{
				complex t1=a[k],t2=w*a[k+i/2];
				a[k]=t1+t2,a[k+i/2]=t1-t2;
				w=w*wn;
			}
		}
	}
	if(flg==-1) for(int i=0;i<len;i++) a[i].r/=len;
}
int n,len1,len2;
long long ans[maxn];
char s1[maxn],s2[maxn];
int main()
{
	int i,t;
	scanf("%s%s",s1,s2);
	len1=strlen(s1),len2=strlen(s2);
	for(i=0,t=len1%wei?0:-1;i<len1;i++) {if((len1-i)%wei==0) t++;a1[t].r=a1[t].r*10+(s1[i]-'0');}
	len1=t+1;
	for(i=0,t=len2%wei?0:-1;i<len2;i++) {if((len2-i)%wei==0) t++;a2[t].r=a2[t].r*10+(s2[i]-'0');}
	len2=t+1;
	n=1;while(n<len1+len2) n<<=1;
	change(a1,n),change(a2,n);
	fft(a1,n,1),fft(a2,n,1);
	for(int i=0;i<n;i++) a2[i]=a1[i]*a2[i];
	change(a2,n);
	fft(a2,n,-1);
	for(int i=0;i<len1+len2-1;i++) ans[i]=(long long)(a2[i].r+0.5);
	for(int i=len1+len2-2;i>0;i--)
	{
		ans[i-1]+=ans[i]/bit;
		ans[i]%=bit;
	}
	for(i=0;ans[i]==0&&i<len1+len2-2;i++);
	printf("%lld",ans[i++]);
	while(i<len1+len2-1) printf("%03lld",ans[i++]);
}
NTT:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
#define maxn 400005
using namespace std;
LL mod = 998244353, G = 3;
LL a1[maxn],a2[maxn],w,wn;
int len1,len2;
char s1[maxn],s2[maxn];
inline LL ksm(LL a,int b){
    LL s=1;
    for(;b;b>>=1,a=a*a%mod) if(b&1) s=s*a%mod;
    return s;
}
inline void change(LL *a,int len)
{
    for(int i=1,j=len/2,k;i<len-1;i++)
    {
        if(i<j) swap(a[i],a[j]);
        for(k=len/2;j>=k;j-=k,k>>=1);
        j+=k;
    }
}
inline void ntt(LL *a,int len,int flg)
{
    change(a,len);
    for(int i=2;i<=len;i<<=1)
    {
        if(flg==1) wn=ksm(G,(mod-1)/i);
        else wn=ksm(G,mod-1-(mod-1)/i);
        for(int j=0;j<len;j+=i)
        {
            w=1;
            for(int k=j;k<j+i/2;k++)
            {
                LL u=a[k],v=w*a[k+i/2]%mod;
                a[k]=(u+v)%mod,a[k+i/2]=(u-v+mod)%mod;
                w=w*wn%mod;
            }
        }
    }
    if(flg==-1){
        LL ni=ksm(len,mod-2);
        for(int i=0;i<len;i++) a[i]=a[i]*ni%mod;
    }
}
int main()
{
    scanf("%s%s",s1,s2);
    len1=strlen(s1),len2=strlen(s2);
    for(int i=0;i<len1;i++) a1[i]=s1[i]-'0';
    for(int i=0;i<len2;i++) a2[i]=s2[i]-'0';
    int len=1;while(len<len1+len2-1) len<<=1;
    ntt(a1,len,1),ntt(a2,len,1);
    for(int i=0;i<len;i++) a2[i]=a1[i]*a2[i]%mod;
    ntt(a2,len,-1);
    for(int i=len1+len2-2;i>0;i--) a2[i-1]+=a2[i]/10,a2[i]%=10;
    int i;
    for(i=0;i<len1+len2-2&&a2[i]==0;i++);
    printf("%lld",a2[i++]);
    while(i<len1+len2-1) printf("%lld",a2[i++]);
}

其实两个方法构造出的根都是类似于 ω n i = x i n \omega _n^i=x^{\frac in} ωni=xni,x随i从0到n-1呈周期性变化,满足 ω n n 2 = − 1 \omega_n^{\frac n2}=-1 ωn2n=1 ω 2 n 2 i = ω n i \omega_{2n}^{2i}=\omega_n^i ω2n2i=ωni
从而将 A ( x ) = a 0 + a 1 x + ⋯ + a n − 1 x n − 1 A(x)=a_0+a_1x+\dots+a_{n-1}x^{n-1} A(x)=a0+a1x++an1xn1的求值问题分治为
A 0 ( x ) = a 0 + a 2 x + a 4 x 2 + ⋯ + a n − 2 x n 2 − 1 A 1 ( x ) = a 1 + a 3 x + a 5 x 2 + ⋯ + a n − 1 x n 2 − 1 A ( x ) = A 0 ( x 2 ) + x A 1 ( x 2 ) A_0(x)=a_0+a_2x+a_4x^2+\dots+a_{n-2}x^{\frac n2-1 }\\ A_1(x)=a_1+a_3x+a_5x^2+\dots+a_{n-1}x^{\frac n2-1}\\ A(x)=A_0(x^2)+xA_1(x^2) A0(x)=a0+a2x+a4x2++an2x2n1A1(x)=a1+a3x+a5x2++an1x2n1A(x)=A0(x2)+xA1(x2)
x 2 在 x = ω n i 和 x = ω n i + n 2 x^2在x=\omega_n^i和x=\omega_n^{i+\frac n2} x2x=ωnix=ωni+2n处是相等的,n个根的求值问题就转化成了2个n/2个根的求值问题
FFT原理学习


Upd:
胡小兔的blog
FFT的代码技巧

n n n 次单位根存在 [ n 2 , n − 1 ] [\frac n2,n-1] [2n,n1] 的位置上,对 n 2 , n 4 . . . \frac n2,\frac n4... 2n,4n... 同理,于是对于 i ∈ [ 0 , 2 n ) i\in [0,\frac 2n) i[0,n2) w i = w i < < 1 w_i=w_{i<<1} wi=wi<<1

对于 IDFT 可以翻转 a 1... n − 1 a_{1...n-1} a1...n1,( a 0 a_0 a0 不翻转),可以发现第 i i i 个位置的值是 a 0 + ∑ j = 1 n − 1 a n − j w n i j a_0+\sum_{j=1}^{n-1}a_{n-j}w_n^{ij} a0+j=1n1anjwnij,它与 a 0 + ∑ j = 1 n − 1 a j w n − i j a_0+\sum_{j=1}^{n-1}a_jw_{n}^{-ij} a0+j=1n1ajwnij 相等。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值