这篇文章会讲讲FFT的原理和代码。

先贴picks博客(又名FFT从入门到精通):http://picks.logdown.com/posts/177631-fast-fourier-transform

首先FFT是干嘛用的?

额其实在oi中它就是一个用来算“快速卷积”的东西。

卷积是啥?

给定两个数组a、b,求数组c使得:

for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
if(i+j<n) c[i+j]+=a[i]*b[j];

这就叫做长度为n的“卷积”。

正常模拟是O(n^2)的,这时候我们就可以用FFT来加速到O(nlogn)!

我们发现,如果我们令a[i]为x^i的系数,那么a、b就可以表示为一个多项式,c就可以被表示为这两个多项式的乘积。

首先我们可以发现,我们对于一个n次多项式,可以用一个多项式的形式来表示它,也可以找到n个位置的值,这样也可以唯一确定这个多项式。

所以我们就初步有了一个思路,我们找到a、b在n个点处的取值,乘在一起,搞回去确定c的多项式形式。

为了和谐,我们一般令n为2的次幂。(注意)

关于这个东西一般有两种写法,一般被称为复数FFT和NTT。

先讲NTT好了......

假设a、b都是整系数多项式,然后模数P十分刺激,满足P是质数,$2^k|P-1$且$2^k>n$时,我们就可以使用NTT。

然后你还要知道原根的有关概念...简单来说就是原根的次幂在模P意义下循环节为$\varphi(P)$,对于素数来说就是P-1。

这里就说一点,998244353的原根是3...

设g为P的原根,那么我们令$\omega_n=g^{\frac{P-1}{n}}$,可以发现:

$\omega_{2n}^{2m}=\omega_{n}^m$,$\omega_{2n}^m=-\omega_{2n}^{m+n}$。(确实挺显然的)

那么我们取$\omega_n^k$,其中k∈{0...n-1},作为n个点,如何算出这n个点处的取值呢?

我们假设偶次项提出来作为a0,奇次项提出来作为a1。

(例如1+2x+3x^2+4x^3,偶次项提出来为1+3x,奇次项提出来为2+4x,注意这里的次数也要相应改变)

那么我们可以发现

所以我们可以用a0和a1的点值表示算出a的点值表示。

T(n)=2T(n/2)+O(n),由主定理复杂度为O(nlogn)。

接下来转回去的话,由于某种奇怪的性质(详细证明可以看picks博客),我们只要用$\omega_{n}^{-m}$代替原来的$\omega_n^{m}$,带进去,最后除以n就行了。即把那一堆$\omega$翻转一下。

当然如果你真这样瞎搞常数似乎真的挺大的,事实上有一些更靠谱的做法,上图:

开始我们把输入的数二进制位翻转,就可以得到左边,然后按这个图上进行蝶形运算(就是刚才那两个公式)就可以算出结果了。

额复数FFT更加简单。

我们令$\omega_{n}$为单位根,即满足$x^n=1$的复数,它可以看做复平面上x轴正方向绕逆时针方向旋转$\frac{2\pi}{n}$的复数。所以$\omega_n=cos(\frac{2\pi}{n})+sin(\frac{2\pi}{n})i$。

听起来十分靠谱...但是这种东西毕竟自己瞎写的话常数实在太大了...

下面这个是n+e的NTT模板,有改动,uoj34:

#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
using namespace std;
#define ll long long
ll MOD=998244353;
ll w[2][666666];
ll qp(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b&1) ans=ans*a%MOD;
a=a*a%MOD; b>>=1;
}
return ans;
}
int K;
void fftinit(int n)
{
for(K=1;K<n;K<<=1);
w[0][0]=w[0][K]=1;
ll g=qp(3,(MOD-1)/K); //3是原根
for(int i=1;i<K;i++) w[0][i]=w[0][i-1]*g%MOD;
for(int i=0;i<=K;i++) w[1][i]=w[0][K-i];
}
void fft(int* x,int v)
{
for(int i=0,j=0;i<K;i++)
{
if(i>j) swap(x[i],x[j]);
for(int l=K>>1;(j^=l)<l;l>>=1);
}
for(int i=2;i<=K;i<<=1)
{
for(int j=0;j<K;j+=i)
{
for(int l=0;l<i>>1;l++)
{
ll t=(ll)x[j+l+(i>>1)]*w[v][K/i*l]%MOD;
x[j+l+(i>>1)]=(x[j+l]-t+MOD)%MOD;
x[j+l]=(x[j+l]+t)%MOD;
}
}
}
if(!v) return;
ll rv=qp(K,MOD-2);
for(int i=0;i<K;i++) x[i]=x[i]*rv%MOD;
}
int N,M,a[666666],b[666666],c[666666];
int main()
{
scanf("%d%d",&N,&M);
++N; ++M; int t=N+M-1;
for(int i=0;i<N;i++) scanf("%d",a+i);
for(int i=0;i<M;i++) scanf("%d",b+i);
fftinit(t); fft(a,0); fft(b,0);
for(int i=0;i<K;i++) c[i]=(ll)a[i]*b[i]%MOD;
fft(c,1);
for(int i=0;i<t;i++) printf("%d ",c[i]);
}

FFT入门的更多相关文章

  1. TOT 傅立叶变换 FFT 入门

    HDU 1402,计算很大的两个数相乘. FFT 只要78ms,这里: 一些FFT 入门资料:http://wenku.baidu.com/view/8bfb0bd476a20029bd642d85. ...

  2. 洛谷p3803 FFT入门

    洛谷p3803 FFT入门 ps:花了我一天的时间弄懂fft的原理,感觉fft的折半很神奇! 大致谈一谈FFT的基本原理: 对于两个多项式的卷积,可以O(n^2)求出来(妥妥的暴力) 显然一个多项式可 ...

  3. hdu1402 FFT入门

    参考这里:http://www.cnblogs.com/pdev/p/4354705.html  http://www.cnblogs.com/pdev/p/4354629.html 题意:求大数乘法 ...

  4. FFT 入门

    推荐博客 :https://oi.men.ci/fft-notes/ 卷积的理解 : https://www.zhihu.com/question/22298352?rf=21686447 题目链接 ...

  5. bzoj2179: FFT快速傅立叶

    #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> ...

  6. 多项式FFT相关模板

    自己码了一个模板...有点辛苦...常数十分大,小心使用 #include <iostream> #include <stdio.h> #include <math.h& ...

  7. 3-idiots hdu4609 母函数+FFT 组合数学题

    http://acm.hdu.edu.cn/showproblem.php?pid=4609 题意:1e5个数,求取三个数能形成三角形的概率. 题解(这怎么会是fft入门题QAQ): 概率的算法就是三 ...

  8. 模板:快速傅里叶变换(FFT)

    参考:http://blog.csdn.net/f_zyj/article/details/76037583 如果公式炸了请去我的csdn博客:http://blog.csdn.net/luyouqi ...

  9. HDU 1402 大数乘法 FFT、NTT

    A * B Problem Plus Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Other ...

随机推荐

  1. Android属性之build.prop生成过程分析

    Android的build.prop文件是在Android编译时刻收集的各种property(LCD density/语言/编译时间, etc.),编译完成之后,文件生成在out/target/pro ...

  2. 什么是java path环境变量

    参考:https://docs.oracle.com/javase/tutorial/essential/environment/paths.html 从orcle官网的文档中可以看到java pat ...

  3. ArrayList Vector LinkedList 区别与用法

    转载自: http://www.cnblogs.com/mgod/archive/2007/08/05/844011.html 最近用到了,所以依然是转载 ArrayList 和Vector是采用数组 ...

  4. SQL Server 2012实施与管理实战指南(笔记)——Ch6连接的建立和问题排查

    6.连接的建立和问题排查 会话的建立分成2个部分: 1.连接,即找到这个实例 2.认证,告诉sql server谁要连接 目录 6.连接的建立和问题排查 6.1协议选择和别名 6.1.1 服务器网络配 ...

  5. spring hibernate摘记

    一.spring 1.ContextLoaderListener    它作用就是启动Web容器时,自动装配ApplicationContext的配置信息.因为它实现了ServletContextLi ...

  6. Servlet/JSP-06 Session

    一. 概述 Session 指客户端(浏览器)与服务器端之间保持状态的解决方案,有时候也用来指这种解决方案的存储结构. 当服务器端程序要为客户端的请求创建一个 Session 时,会首先检查这个请求里 ...

  7. 探究C语言中的前++和后++

    小波带您探究c语言中的前++与后++: 欢迎吐槽,欢迎加QQ463431476. 欢迎关注!  现在来探究: 咱们先看第一个 i被赋值0,i++(后++)并没有输出1.   现在i被赋值0,++i,也 ...

  8. iframe 的使用和登陆退出的实现——整个页面跳转

    iframe中如果只是页面跳转的话,我们依然只是部分的加载的了,为了实现整个页面的所有内容跳转,下面提供了整个页面跳转的方法. iframe例子 1.总的iframe页面(访问就访问这个)  all. ...

  9. C# .NET 动态调用webservice的三种方式

    转载自 百度文库 http://wenku.baidu.com/link?url=Q2q50wohf5W6UX44zqotXFEe_XOMaib4UtI3BigaNwipOHKNETloMF4ax4W ...

  10. Linux autojump命令

    一.简介 autojump是一个命令行工具,它允许你可以直接跳转到你喜爱的目录,而不用管你现在身在何处. 二.安装 yum install autojump 三.用法 j [目录的名字或名字的一部分] ...