$FFT$总结

(因为还不会啊,,都没做过什么题,所以一边学一边打咯。。

1、主要是用来加速卷积形式的求和吧?

  $F*G(n)=F[i] × G[n-i]$

  平时是$O(n^2)$的,FFT可以$O(nlogn)$

2、相当于求两个多项式的乘积(你要求的函数是其系数)

  $A(x)=A0+A1*x+A2*x^2+A3*x^3+...+An−1*x^{n−1}$

  $B(x)=B0+B1*x+B2*x^2+B3*x^3+...+Bm−1*x^{m−1}$

3、具体步骤?

  系数表达->点值表达->相乘->点值表达->系数表达

4、点值表示法

把多项式$A(x)$代入若干个x的值得到若干个点$(x0,A(x0)),(x1,A(x1)),(x2,A(x2)),...,(xn−1,A(xn−1))$

  我们把从点值表达式转化为系数表达式的操作称为插值

 [点值表示法对应系数表示法是有唯一性的]

5、n次单位复根

  n次单位复根是满足w^n=1的复数w,有n个。他们均匀分布在以复平面的原点为圆心的单位圆上

  为$e^{\dfrac{2πki}{n}}$

  复数幂的定义$e^{ui}=cos(u)+isin(u)$

不如看这里

很详细的。

于是略过。

递归式模板

#include<cstdio>
#include<iostream>
#include<cmath>
#include<memory.h>
#define N 400010
using namespace std;
const double pi=acos(-1); struct P
{
double x,y;
P() {x=y=0;}
P(double x,double y):x(x),y(y){}
}a[N],b[N]; P operator + (P x,P y) {return P(x.x+y.x,x.y+y.y);}
P operator - (P x,P y) {return P(x.x-y.x,x.y-y.y);}
P operator * (P x,P y) {return P(x.x*y.x-x.y*y.y,x.x*y.y+x.y*y.x);} void fft(P *s,int n,int t)
{
if(n==1) return;
P a0[n>>1],a1[n>>1];
for(int i=0;i<=n;i+=2) a0[i>>1]=s[i],a1[i>>1]=s[i+1];
fft(a0,n>>1,t);fft(a1,n>>1,t);
P wn(cos(2*pi/n),t*sin(2*pi/n)),w(1,0);
for(int i=0;i<(n>>1);i++,w=w*wn) s[i]=a0[i]+w*a1[i],s[i+(n>>1)]=a0[i]-w*a1[i];
//w^2=(w+(n>>1))^2 均匀分布在圆上面?
//w[i^2,n]=w[i/2,n/2] 折半引理
//s[i]=a0’(i^2)+i*a1’(i^2)=a0(i)+i*a1(i)
//s[i+n>>1]=a0’((i+n>>1)^2)+i*a1’((i+n>>1)^2)=a0’(i^2)-i*a1’(i^2)
//因为i=-(i+n>>1) 折半引理
} int main()
{
int n,m,nn;
scanf("%d%d",&n,&m);
memset(a,0,sizeof(a));memset(b,0,sizeof(b));
for(int i=0;i<=n;i++) scanf("%lf",&a[i].x);
for(int i=0;i<=m;i++) scanf("%lf",&b[i].x);
nn=1;while (nn<=n+m) nn<<=1;
fft(a,nn,1);fft(b,nn,1);
for(int i=0;i<=nn;i++) a[i]=a[i]*b[i];
fft(a,nn,-1);
for(int i=0;i<=n+m;i++) printf("%d ",(int)(a[i].x/nn+0.5));
return 0;
}

  

迭代式模板

高效实现$FFT$

也就是使用迭代代替递归

比如n=8,R数组长这样:

$a0,a4,a2,a6,a1,a5,a3,a7$

也称作位逆序置换

 

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<complex>
#define Maxn 262145
#define pi acos(-1)
using namespace std; struct P
{
double x,y;
P() {x=y=0;}
P(double x,double y):x(x),y(y){}
friend P operator + (P x,P y) {return P(x.x+y.x,x.y+y.y);}
friend P operator - (P x,P y) {return P(x.x-y.x,x.y-y.y);}
friend P operator * (P x,P y) {return P(x.x*y.x-x.y*y.y,x.x*y.y+x.y*y.x);}
}a[Maxn],b[Maxn]; int nn;
int R[Maxn];
void fft(P *a,int f)
{
for(int i=0;i<nn;i++) if(i<R[i]) swap(a[i],a[R[i]]);
for(int i=1;i<nn;i<<=1)
{
P wn(cos(pi/i),f*sin(pi/i));
for(int j=0;j<nn;j+=i<<1)
{
P w(1,0);
for(int k=0;k<i;k++,w=w*wn)
{
P x=a[j+k],y=w*a[j+k+i];
a[j+k]=x+y;a[j+k+i]=x-y;
}
}
}
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=0;i<=n;i++) scanf("%lf",&a[i].x);
for(int i=0;i<=m;i++) scanf("%lf",&b[i].x);
int ll=0;nn=1;
while(nn<=n+m) ll++,nn<<=1;
for(int i=0;i<nn;i++) R[i]=(R[i>>1]>>1)|((i&1)<<(ll-1));
fft(a,1);fft(b,1);
for(int i=0;i<=nn;i++) a[i]=a[i]*b[i];
fft(a,-1);
for(int i=0;i<=n+m;i++) printf("%d ",(int)(a[i].x/nn+0.5));
return 0;
}

  


$NTT$

简单复习一下原根

  1、模p下原根g,即g在模p下的阶为$\varphi(p)$

  2、g的幂构成模p下的缩系。【很有用!

  3、p有原根当且仅当 p=2,4,质数^a,2*质数^a

  4、求原根的方法:

    暴力枚举判断,先找出最小的一个。

    判断方法:对$\varphi(p)$质因数分解,假设为$p1^{r1}*p2^{r2}...*pn^{rn}$

         有恒有 $g^{\dfrac{\varphi(p)}{pi}}!=1 (Mod P)$成立,则g是p的原根,否则不是。

    假设最小原根是$g$,则当$gcd(d,\varphi(p))==1$时,$g^d$也是模p下的原根。

    即模p下原根个数为$\varphi(\varphi(p))$

对于$NTT$的题目,原根代替了n次单位复根,作用大致相同。只需把一些地方改成Mod之类的就可以。

具体看这里 【这篇写得太好了

当模数不符合要求?

具体实现方式(不完整代码,只放主要部分)

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define Maxn 500010
#define LL long long
const int Mod=998244353;
const int G=3; int nn,R[Maxn],inv;
void ntt(int *s,int f)
{
for(int i=0;i<nn;i++) if(i<R[i]) swap(s[i],s[R[i]]);
for(int i=1;i<nn;i<<=1)
{
int wn=qpow(G,(Mod-1)/(i<<1));
for(int j=0;j<nn;j+=(i<<1))
{
int w=1;
for(int k=0;k<i;k++)
{
int x=s[j+k],y=1LL*w*s[j+k+i]%Mod;
s[j+k]=(x+y)%Mod;s[j+k+i]=((x-y)%Mod+Mod)%Mod;
w=1LL*w*wn%Mod;
}
}
}
if(f==-1)
{
reverse(s+1,s+nn);
for(int i=0;i<=nn;i++) s[i]=1LL*s[i]*inv%Mod;
}
} int main()
{
///////////////////////////////
nn=1;int ll=0;
while(nn<=2*n) nn<<=1,ll++;
for(int i=0;i<=nn;i++) R[i]=(R[i>>1]>>1)|((i&1)<<(ll-1));
inv=qpow(nn,Mod-2);
ntt(a,1);ntt(b,1);
for(int i=0;i<=nn;i++) a[i]=1LL*a[i]*b[i]%Mod;
ntt(a,-1);
///////////////////////////////
return 0;
}

  

好啦!!!

其实有很多地方是要证明的,但是我都不会 先记住吧。。


多项式求逆 + 多项式开根

多项式求逆的代码实现

void get_inv(int *a,int *b,int len)
{
static int temp[Maxn];
if(len==1)
{
b[0]=qpow(a[0],Mod-2);
b[1]=0;
return;
}
get_inv(a,b,len>>1);
memcpy(temp,a,sizeof(int)*len);
memset(temp+len,0,sizeof(int)*len);
NTT(temp,len<<1,1),NTT(b,len<<1,1);
for(int i=0;i<(len<<1);i++)
b[i]=1LL*b[i]*(2-1LL*temp[i]*b[i]%Mod+Mod)%Mod;
NTT(b,len<<1,-1);
memset(b+len,0,sizeof(int)*len);
}

len为当前模数。不断二分。

2017-04-14 15:00:52

【FFT&NTT 总结】的更多相关文章

  1. FFT \ NTT总结(多项式的构造方法)

    前言.FFT  NTT 算法 网上有很多,这里不再赘述. 模板见我的代码库: FFT:戳我 NTT:戳我 正经向:FFT题目解题思路 \(FFT\)这个玩意不可能直接裸考的..... 其实一般\(FF ...

  2. [学习笔记&教程] 信号, 集合, 多项式, 以及各种卷积性变换 (FFT,NTT,FWT,FMT)

    目录 信号, 集合, 多项式, 以及卷积性变换 卷积 卷积性变换 傅里叶变换与信号 引入: 信号分析 变换的基础: 复数 傅里叶变换 离散傅里叶变换 FFT 与多项式 \(n\) 次单位复根 消去引理 ...

  3. FFT/NTT/MTT学习笔记

    FFT/NTT/MTT Tags:数学 作业部落 评论地址 前言 这是网上的优秀博客 并不建议初学者看我的博客,因为我也不是很了解FFT的具体原理 一.概述 两个多项式相乘,不用\(N^2\),通过\ ...

  4. FFT&NTT总结

    FFT&NTT总结 一些概念 \(DFT:\)离散傅里叶变换\(\rightarrow O(n^2)\)计算多项式卷积 \(FFT:\)快速傅里叶变换\(\rightarrow O(nlogn ...

  5. 快速构造FFT/NTT

    @(学习笔记)[FFT, NTT] 问题概述 给出两个次数为\(n\)的多项式\(A\)和\(B\), 要求在\(O(n \log n)\)内求出它们的卷积, 即对于结果\(C\)的每一项, 都有\[ ...

  6. FFT/NTT模板 既 HDU1402 A * B Problem Plus

    @(学习笔记)[FFT, NTT] Problem Description Calculate A * B. Input Each line will contain two integers A a ...

  7. FFT/NTT基础题总结

    在学各种数各种反演之前把以前做的$FFT$/$NTT$的题整理一遍 还请数论$dalao$口下留情 T1快速傅立叶之二 题目中要求求出 $c_k=\sum\limits_{i=k}^{n-1}a_i* ...

  8. $FFT/NTT/FWT$题单&简要题解

    打算写一个多项式总结. 虽然自己菜得太真实了. 好像四级标题太小了,下次写博客的时候再考虑一下. 模板 \(FFT\)模板 #include <iostream> #include < ...

  9. FFT&NTT数学解释

    FFT和NTT真是噩梦呢 既然被FFT和NTT坑够了,坑一下其他的人也未尝不可呢 前置知识 多项式基础知识 矩阵基础知识(之后会一直用矩阵表达) FFT:复数基础知识 NTT:模运算基础知识 单位根介 ...

  10. HDU-4609(FFT/NTT)

    HDU-4609(FFT/NTT) 题意: 给出n个木棒,现从中不重复地选出3根来,求能拼出三角形的概率. 计算合法概率容易出现重复,所以建议计算不合法方案数 枚举选出的最大边是哪条,然后考虑剩下两条 ...

随机推荐

  1. vue+webpack开发(一)

    一开始接触这个vue+webpack的时候,实在是摸不着头脑,根本无从下手. 但是经过这两天的研究,其实你会发现vue其实并不难,难度都在webpack你对webpack的理解. webpack顾名思 ...

  2. libvirt工具实现虚拟机管理

    libvirt工具实现虚拟机管理 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.介绍virt-install命令的常用参数 virt-install是一个命令行工具,它能够为K ...

  3. bzoj千题计划211:bzoj1996: [Hnoi2010]chorus 合唱队

    http://www.lydsy.com/JudgeOnline/problem.php?id=1996 f[i][j][0/1] 表示已经排出队形中的[i,j],最后一个插入的人在[i,j]的i或j ...

  4. 第一、介绍Canvas

    canvas能做什么? canvas是HTML5中的新元素,你可以使用javascript用它来绘制图形.图标.以及其它任何视觉性图像.它也可用于创建图片特效和动画.如果你掌握了完整的命令,你可以用c ...

  5. 【NOI】2017 蚯蚓排队(BZOJ 4943,LOJ 2303) 模拟+hash

    [题目]#2303. 「NOI2017」蚯蚓排队 [题意]给定n条长度不超过6的蚯蚓,初始各自在一个队伍.m次操作:1.将i号蚯蚓和j号蚯蚓的队伍合并(保证i为队尾,j为队首).2.将i号蚯蚓和它后面 ...

  6. linux网络配置原理

    一.网络连接的基本原理 http://www.cnblogs.com/dyllove98/archive/2013/08/06/3241294.html

  7. How to become a successful bug bounty hunter

    出处:https://www.hackerone.com/blog/become-a-successful-bug-bounty-hunter 如果你梦想成为赏金猎人,你的梦想就会成真 - 不要把你的 ...

  8. [HNOI2007]梦幻岛宝珠 「套路:分层 $DP$」

    显然直接 \(01\) 背包会超时并且超空间 套路:分层 \(DP\) 「考虑将每个子结构看作一层(也就是包含了不止 \(1\) 个物品的信息),并且大层不会对小层造成影响,可以考虑先进行每一层的自我 ...

  9. 09 Go 1.9 Release Notes

    Go 1.9 Release Notes Introduction to Go 1.9 Changes to the language Ports ppc64x requires POWER8 Fre ...

  10. Grafana 短信报警

    一.分析 需求 Grafana支持短信渠道报警 要求 使用开发提供的短信API接口 请求url: http://192.168.1.1:8088/alerting/sendSms?mobile=手机号 ...