zoukankan      html  css  js  c++  java
  • NTT FFT 实现

    FFT 实现

    蝴蝶操作

    [A(omega_n^k) = A_1(w_{frac{n}{2}}^k) + omega_n^k A_2(w_{frac{n}{2}}^k) ]

    [A(omega_n^{k+frac{n}{2}}) = A_1(w_{frac{2}{n}}^k) - omega_n^k A_2(w_{frac{n}{2}}^k) ]

    那么我们可以不用每次都计算一次 (omega_n^k A_2(w_{frac{n}{2}}^k))

    直接将其记录,(O(1)) 求出对应项即可

    二进制反转

    对于一个多项式原序列的下标的递归树

    初始:(0,1,2,3,4,5,6,7)

    递归结束: (0,4,2,6,1,5,3,7)

    将上述结果利用二进制写出:初始时: (000,001,010,011,100,101,110,111)

    递归结束: (000,100,010,110,001,101,011,111)

    显然,最后的递归到的下标就是原下标的二进制反转形式

    所以我们可以直接预处理每一位的二进制反转后的情况,在迭代实现的时候直接调用就行了

    Code

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<math.h>
    #include<queue>
    #include<cmath>
    #include<climits>
    #define ll int
    #define ld long double
    
    inline ll read()
    {
    	ll x=0,f=1;
    	char ch=getchar();
    	while(!isdigit(ch))
    	{
    		if(ch=='-') f=-1;
    		ch=getchar();
    	}
    	while(isdigit(ch))
    	{
    		x=(x<<1)+(x<<3)+ch-'0';
    		ch=getchar();
    	}
    	return x*f;
    }
    
    const ll maxn=5e6+10;
    const ld pi=acos(-1.0);
    ll n,m,lim=1,cnt;
    ll r[maxn];
    
    struct complex
    {
    	ld x,y;
    	complex(ld a=0,ld b=0){x=a,y=b;}
    } a[maxn],b[maxn];
    
    complex operator + (complex a,complex b)
    {
    	complex c;
    	c.x=a.x+b.x;
    	c.y=a.y+b.y;
    	return c;
    }
    
    complex operator - (complex a,complex b)
    {
    	complex c;
    	c.x=a.x-b.x;
    	c.y=a.y-b.y;
    	return c;
    }
    
    complex operator * (complex a,complex b)
    {
    	complex c;
    	c.x=a.x*b.x-a.y*b.y;
    	c.y=a.x*b.y+a.y*b.x;
    	return c;
    }
    
    inline void fft(complex *s,ll flag)
    {
    	for(int i=0;i<lim;i++)
    	{
    		if(i<r[i]) std::swap(s[i],s[r[i]]);
    	}
    	
    	for(int i=1;i<lim;i<<=1)
    	{	
    		complex w(cos(pi/i),flag*sin(pi/i));
    		
    		for(int R=i<<1,L=0;L<lim;L+=R)
    		{
    			complex x(1,0);
    			
    			for(int k=0;k<i;k++,x=x*w)
    			{
    				complex A=s[k+L],B=x*s[k+L+i];
    				s[L+k]=A+B;
    				s[L+k+i]=A-B;
    			}
    		}
    	}
    }
    
    int main(void)
    {
    	n=read(),m=read();
    	
    	for(int i=0;i<=n;i++) a[i].x=read();
    	for(int i=0;i<=m;i++) b[i].x=read();
    	
    	while(lim<=m+n)
    	{
    		lim<<=1;
    		cnt++;
    	}
    	
    	for(int i=0;i<lim;i++)
    	{
    		r[i]=(r[i>>1]>>1)|((i&1)<<(cnt-1));
    	}
    	
    	fft(a,1);
    	fft(b,1);
    	
    	for(int i=0;i<=lim;i++)
    	{
    		a[i]=b[i]*a[i];
    	}
    	
    	fft(a,-1);
    	
    	for(int i=0;i<=n+m;i++)
    	{
    		printf("%d ",(int)(a[i].x/(1.0*lim)+0.5));
    	}
    	return 0;
    }
    

    NTT 实现

    与 FFT 同理

    这里使用原根的概念,假设有一个质数 (p) 恰好是 (a imes 2^k+1) 的形式,其原根为 (g)

    那么每次迭代求解的时候,只需要将 (omega = g^{frac{p-1}{n}}) 即可

    对于 IDFT 操作,直接将 (omega) 赋值为 (inv[g]^{frac{p-1}{n}}) 即可

    此处 (inv[g]) 表示 (g) 的逆元

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<math.h>
    #include<queue>
    #include<climits>
    #define ll long long
    #define ld long double
    
    inline ll read()
    {
    	ll x=0,f=1;
    	char ch=getchar();
    	while(!isdigit(ch))
    	{
    		if(ch=='-') f=-1;
    		ch=getchar();
    	}
    	while(isdigit(ch))
    	{
    		x=(x<<1)+(x<<3)+ch-'0';
    		ch=getchar();
    	}
    	return x*f;
    }
    
    const ll mod=998244353,yg=3;
    const ll maxn=5e6+10;
    ll n,m,lim=1,cnt,yg_inv;
    ll a[maxn],b[maxn],r[maxn];
    
    inline ll ksm(ll a,ll b,ll p)
    {
    	ll ret=1;
    	while(b)
    	{
    		if(b&1) ret=ret*a%p;
    		a=a*a%p;
    		b>>=1;
    	}
    	return ret%p;
    }
    
    inline void NTT(ll *s,ll flag)
    {
    	for(int i=0;i<lim;i++)
    	{
    		if(i<r[i]) std::swap(s[i],s[r[i]]);
    	}
    	
    	for(ll mid=1;mid<lim;mid<<=1)
    	{
    		ll w=ksm(flag==1 ? yg : yg_inv,(mod-1)/(mid<<1),mod);
    		
    		for(ll R=(mid<<1),j=0;j<lim;j+=R)
    		{
    			ll x=1;
    			
    			for(ll k=0;k<mid;k++,x=x*w%mod)
    			{
    				ll A=s[k+j]%mod,B=x%mod*s[k+j+	mid]%mod;
    				s[k+j]=(A+B)%mod;
    				s[k+j+mid]=(A-B+mod)%mod;
    			}
    		}
    	}
    }
    
    int main(void)
    {
    	n=read(),m=read();
    	
    	yg_inv=ksm(3ll,mod-2,mod);
    	
    	for(int i=0;i<=n;i++) a[i]=read();
    	for(int i=0;i<=m;i++) b[i]=read();
    	while(lim<=n+m)
    	{
    		lim<<=1;
    		cnt++;
    	}
    	
    	for(ll i=0;i<lim;i++)
    	{
    		r[i]=(r[i>>1]>>1)|((i&1)<<(cnt-1));
    	}
    	
    	NTT(a,1);
    	NTT(b,1);
    	 
    	for(int i=0;i<lim;i++) a[i]=a[i]*b[i]%mod;
    	
    	NTT(a,0);
    	
    	ll inv=ksm(lim,mod-2,mod);
    	
    	for(int i=0;i<=n+m;i++)
    	{
    		printf("%lld ",(a[i]*inv+mod)%mod);
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    FZU 2098 刻苦的小芳(卡特兰数,动态规划)
    卡特兰数总结
    FZU 1064 教授的测试(卡特兰数,递归)
    HDU 4745 Two Rabbits(区间DP,最长非连续回文子串)
    Java 第十一届 蓝桥杯 省模拟赛 正整数的摆动序列
    Java 第十一届 蓝桥杯 省模拟赛 反倍数
    Java 第十一届 蓝桥杯 省模拟赛 反倍数
    Java 第十一届 蓝桥杯 省模拟赛 反倍数
    Java 第十一届 蓝桥杯 省模拟赛 凯撒密码加密
    Java 第十一届 蓝桥杯 省模拟赛 凯撒密码加密
  • 原文地址:https://www.cnblogs.com/jd1412/p/14545566.html
Copyright © 2011-2022 走看看