数学板块学习之拆系数FFT

拆系数FFT

比任意模数NTT要快一点的的算法(要看情况,洛谷的一个板子题我NTT跑了1071ms,而FFT跑了4927ms)
取一个 M = 1 &lt; &lt; 15 M=1&lt;&lt;15 M=1<<15
F [ i ] , G [ i ] F[i],G[i] F[i],G[i]变为
F [ i ] = A [ i ] ∗ M + B [ i ] G [ i ] = C [ i ] ∗ M + D [ i ] \begin{aligned}F[i]&amp;=A[i]*M+B[i]\\ G[i]&amp;=C[i]*M+D[i] \end{aligned} F[i]G[i]=A[i]M+B[i]=C[i]M+D[i]

所以 F [ i ] ∗ G [ i ] F[i]*G[i] F[i]G[i]就变为了
F [ i ] ∗ G [ i ] = ( A [ i ] ∗ M + B [ i ] ) ∗ ( C [ i ] ∗ M + D [ i ] ) = A [ i ] ∗ C [ i ] ∗ M 2 + ( B [ i ] ∗ C [ i ] + A [ i ] ∗ D [ i ] ) ∗ M + B [ i ] ∗ D [ i ] \begin{aligned} F[i]*G[i]&amp;=(A[i]*M+B[i])*(C[i]*M+D[i])\\ &amp;=A[i]*C[i]*M^2+(B[i]*C[i]+A[i]*D[i])*M+B[i]*D[i] \end{aligned} F[i]G[i]=(A[i]M+B[i])(C[i]M+D[i])=A[i]C[i]M2+(B[i]C[i]+A[i]D[i])M+B[i]D[i]

所以我们要求的是 A [ i ] ∗ C [ i ] , B [ i ] ∗ C [ i ] , A [ i ] ∗ D [ i ] , B [ i ] ∗ D [ i ] A[i]*C[i],B[i]*C[i],A[i]*D[i],B[i]*D[i] A[i]C[i],B[i]C[i],A[i]D[i],B[i]D[i]
所以最暴力的也就是八次DFT

模板代码(八次DFT):

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <cmath>
#include <string>
#include <cstring>
#include <map>
#include <set>
#include <math.h>
#include <ctime>
#include <unordered_map>
//#include <tr1/unordered_map>

using namespace std;
#define Time_begin time_t begin,end;begin=clock()
#define Time_end end=clock();cout<<"runtime:   "<<double(end-begin)/CLOCKS_PER_SEC*1000<<"ms"<<endl
#define me(x,y) memset(x,y,sizeof x)
#define MIN(x,y) x < y ? x : y
#define MAX(x,y) x > y ? x : y

typedef long long ll;
typedef unsigned long long ull;

const long double INF = 0x3f3f3f3f;
const int MOD = 1e9+7;
const double eps = 1e-06;
const long double PI = std::acos(-1);
const int M=32768;
const int MAXN = 5e5+100;


struct Complex{
	long double x,y;//实部和虚部 x+yi
	Complex(long double _x = 0.0,long double _y = 0.0){
		x = _x;
		y = _y;
	}
	Complex operator - (const Complex &b)const{
		return Complex(x - b.x,y - b.y);
	}	
	Complex operator +(const Complex &b)const{
		return Complex(x+b.x,y+b.y);
	}
	Complex operator *(const Complex &b)const{
		return Complex(x*b.x - y*b.y,x*b.y+y*b.x);
	}
};
/*
* 进行 FFT 和 IFFT 前的反转变换。
* 位置 i 和(i 二进制反转后位置)互换
* len 必须为 2 的幂
*/
void change(Complex y[],int len){
	int i,j,k;
	for(i = 1, j = len/2;i <len - 1;i++){
		if(i < j)swap(y[i],y[j]);
		//交换互为小标反转的元素,i<j 保证交换一次
		//i 做正常的 +1,j 左反转类型的 +1, 始终保持 i 和 j 是反转的
		k = len/2;
		while(j >= k){
			j -= k;
			k /= 2;
		}
		if(j < k)j += k;
	}
}
/*
* 做 FFT
* len 必须为2^k形式
* on==1 时是 DFT,on==-1 时是 IDFT
*/
void fft(Complex y[],int len,int on){
	change(y,len);
	for(int h = 2; h <= len; h <<= 1){
		Complex wn(std::cos(-on*2*PI/h),std::sin(-on*2*PI/h));
		for(int j = 0;j < len;j+=h){
			Complex w(1,0);
			for(int k = j;k < j+h/2;k++){
				Complex u = y[k];
				Complex t = w*y[k+h/2];
				y[k] = u+t;
				y[k+h/2] = u - t;
				w = w*wn;
			}
		}
	}
	if(on == -1)
		for(int i = 0;i < len;i++)
			y[i].x /= (double)len;
}
ll ans[MAXN];
int n,m,p;
int F[MAXN],G[MAXN],l=0;
Complex a[MAXN],b[MAXN],c[MAXN],d[MAXN],e[MAXN],f[MAXN],g[MAXN],h[MAXN];

/*
* F[i]=a[i]*M+b[i]
* G[i]=c[i]*M+d[i] 
*/
void mtt(){
    int len=1;
    n += m;
    while(len <= n) len<<=1;
    for(int i = 0; i < n; ++i){
        a[i].x=F[i]/M,b[i].x=F[i]%M;
        c[i].x=G[i]/M,d[i].x=G[i]%M;
    }
    fft(a,len,1),fft(b,len,1),fft(c,len,1),fft(d,len,1);
    for(int i = 0 ; i < len; ++i){
        e[i]=a[i]*c[i],f[i]=a[i]*d[i];
        g[i]=b[i]*c[i],h[i]=b[i]*d[i];
    }
    fft(e,len,-1),fft(f,len,-1),fft(g,len,-1),fft(h,len,-1);
    for(int i = 0; i < len; ++i){
        ans[i]=0;
        ans[i]=(ans[i] + (ll)(round(e[i].x))%p*M%p*M%p)%p;
        ans[i]=(ans[i] + (ll)(round(f[i].x))%p*M%p)%p;
        ans[i]=(ans[i] + (ll)(round(g[i].x))%p*M%p)%p;
        ans[i]=(ans[i] + (ll)(round(h[i].x))%p)%p;
    }

}

int main() {
    // Time_begin;
    cout<<(1<<15)<<endl;
    scanf("%d%d%d",&n,&m,&p);
    for(int i = 0; i <= n; ++i) scanf("%d",&F[i]);
    for(int i = 0; i <= m; ++i) scanf("%d",&G[i]);
    mtt();
    for(int i = 0; i <= n; ++i){
        printf("%lld%c",ans[i],i == n ? '\n' : ' ');
    }
    // Time_end;
    return 0;
}

/*

*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值