首页 > 其他分享 >FFT&NTT学习笔记

FFT&NTT学习笔记

时间:2023-05-04 18:37:05浏览次数:37  
标签:frac int sum FFT NTT 笔记 include omega 2i

概念

多项式乘法时,我们发现暴力乘十分缓慢,但是点值乘十分快速。考虑求 \(A\) 和 \(B\) 的卷积。

一个 \(n\) 次多项式可以被 \(n+1\) 个点确定。

设多项式 \(A(x)\) 的系数为 \((a_0,a_1,\cdots,a_n)\)

对其奇偶分类得 \(A(x)=\sum\limits a_{2i}*x^{2i}+\sum a_{2i+1}*x^{2i+1}\)

提取 \(n\) 得 \(A(x)=\sum\limits a_{2i}*x^{2i}+x*\sum a_{2i+1}*x^{2i}\)

化简得 \(A(x)=\sum\limits a_{2i}*(x^2)^i+x*\sum a_{2i+1}*(x^2)^i\)

设 \((\frac{n}{2}-1)\) 次多项式 \(A_0=\sum\limits a_{2i}*x^{2i},A_1=\sum\limits a_{2i+1}*x^{2i}\)

则 \(A(x)=A_0(x^2)+x*A_1(x^2)\)

我们发现可以递归

但是时间复杂度不对

然后是一些高深件

复数

定义 \(i^2=-1\)

则所有形如 \(z=a+b*i\) 的数 \(z\) 组成的集合为复数集,记为 \(C\)。而 \(a\) 称为 \(z\) 的实部, \(b\) 为虚部。

复平面

类似坐标系,纵虚横实。复数 \(z\) 对应从原点指向 \((a,b)\) 的向量。幅角为实轴横方向与向量的夹角,记为 \(θ\)。

加法:与向量加法一样

乘法:复数相乘,模长相乘,幅角相加。\((a+bi)*(c+di)=(ac-bd)+(bc+ad)i\)

单位根

在复平面上,以原点为圆心,\(1\) 为半径作圆,所得的圆叫单位圆。以圆点为起点,圆的 \(n\) 等分点为终点,做 \(n\) 个向量,设幅角为正且最小的向量对应的复数为 \(\omega_n\),称为 \(n\) 次单位根。剩余 \(n-1\) 个点则为 \(\omega_n^2,\omega_n^3,\omega_n^4,\cdots\)。

单位根的幅角为周角的 \(\dfrac{1}{n}\)。

欧拉公式有:\(w_n^k=\cos k\frac{2π}{n}+i\sin k\frac{2π}{n}\)

性质 \(1:\) \(\omega_{n}^{k}=\omega_{2n}^{2k}\)

性质 \(2:\) \(\omega _{n}^{k+\frac{n}{2}}=-\omega _{n}^{k}\)

性质 \(3:\) \(w_n^n=1\)

图上自证不难

FFT

承接高深件

高能推柿子

\(A(x)=A_0(x^2)+x*A_1(x^2)\)

带入 \(x=w_n^k(k<\frac{1}{2}n)\) 得

\[A(w_n^k)=A_0(w_n^{2k})+w_n^k*A_1(w_n^{2k}) \]

带入 \(x=w_n^{k+\frac{n}{2}}(k<\frac{1}{2}n)\) 得

\[A(w_n^{k+\frac{n}{2}})=A_0(w_n^{2k+n})+w_n^{k+\frac{n}{2}}*A_1(w_n^{2k+n}) \]

\[A(w_n^k)=A_0(w_{\frac{n}{2}}^{k})+w_n^k*A_1(w_{\frac{n}{2}}^{k}) \]

\[A(w_n^k)=A_0(w_{n}^{2k})-w_n^k*A_1(w_{n}^{2k}) \]

不难发现,两式的值可以互相转换,且两室分别处理了一半区间。递归处理,复杂度 \(O(n\log n)\)。

IFFT

函数转点并相乘后,需要转化回系数。此时使用傅里叶逆变换。

拉差

拉差不行,还是要推柿子。

设向量 \(c_1,c_2,\cdots c_{n-1}\) 满足 \(c_k=\sum\limits^{n}_{j=1} y_j(w_n^{-k})^j\)

推柿子懒了,多项式早日消失

最终

\(c_k=n*a_k\)

\(a_k=\frac{c_k}{n}\)

代码

递归实现即可

使用STL:complex实现复数

但是但是,递归常数很大。

改成迭代就可以了。

尻尻板子:

#include<iostream>
#include<cstdio>
#include<complex>
#include<cmath>
#include<algorithm>
using namespace std;
double pi=acos(-1);
int tax[5000001];
void Rev(int n)
{
	int d=n>>1;
	int len=-1;
	tax[++len]=0;
	tax[++len]=d;
	for(int i=2;i<=n;i<<=1)
	{
		d>>=1;
		for(int p=0;p<i;p++)
		{
			tax[++len]=tax[p]|d;
		}
	}
}
void FFT(complex<double> *A,int N)
{
	for(int i=1;i<N;i++)
	{
		if(tax[i]>i)
		{
			swap(A[i],A[tax[i]]);
		} 
	}
	for(int len=2,M=1;len<=N;M=len,len<<=1)
	{
		complex<double> W(cos(pi/M),sin(pi/M));
		complex<double> w(1.0,0.0);
		for(int l=0,r=len-1;r<=N;l+=len,r+=len)
		{
			complex<double> w0=w;
			for(int p=l;p<l+M;p++)
			{
				complex<double> x=A[p]+w0*A[p+M];
				complex<double> y=A[p]-w0*A[p+M];
				A[p]=x;
				A[p+M]=y;
				w0*=W;
			}
		}
	}
}
void IFFT(complex<double> *A,int N)
{
	FFT(A,N);
	reverse(A+1,A+N);
}
int n,m;
complex<double> F[5000001],G[5000001];
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=0,_;i<=n;i++)
	{
		scanf("%d",&_);
		F[i]=_;
	}
	for(int i=0,_;i<=m;i++)
	{
		scanf("%d",&_);
		G[i]=_;
	}
	int p2=1;
	while(p2<=m+n) p2<<=1;
	Rev(p2);
	FFT(F,p2);
	FFT(G,p2);
	for(int i=0;i<p2;i++)
	{
		F[i]*=G[i];
	}
	IFFT(F,p2);
	for(int i=0;i<=n+m;i++)
	{
		printf("%d ",int(F[i].real()/p2+0.5));
	}
}

NTT板子

#include<iostream>
#include<cstdio>
#include<complex>
#include<cmath>
#include<algorithm>
#define int long long
#define mod 998244353
using namespace std;
double pi=acos(-1);
int tax[5000001];
int n,m;
void Rev(int pn)
{
	for(int i=0;i<pn;i++)
	{
		tax[i]=tax[i>>1]>>1|((i&1)<<(max((int)ceil(log2(n+m)),1ll)-1));
	}
}
long long poww(int a,int b)
{
	int re=1;
	while(b)
	{
		if(b&1)
		{
			re*=a;
			re%=mod;
		}
		a*=a;
		a%=mod;
		b>>=1;
	}
	return re;
}
void NTT(long long *a,int n,int type) //type:1正0反 
{
    for(int i=0;i<n;++i)
    {
        if(i<tax[i])
        {
            swap(a[i],a[tax[i]]);
        }
	}
	for(int i=1;i<n;i<<=1)
    {
        long long gn=poww((type?3:332748118),(mod-1)/(i<<1));
        for(int j=0;j<n;j+=(i << 1))
        {
            long long g0=1;
            for(int k=0;k<i;++k,g0=g0*gn%mod)
            {
                long long x=a[j+k],y=g0*a[i+j+k]%mod;
                a[j+k]=(x+y)%mod;
                a[i+j+k]=(x-y+mod)%mod;
            }
        }
    }
}
long long F[5000001],G[5000001];
signed main()
{
	scanf("%lld%lld",&n,&m);
	for(int i=0,_;i<=n;i++)
	{
		scanf("%lld",&_);
		F[i]=_;
	}
	for(int i=0,_;i<=m;i++)
	{
		scanf("%lld",&_);
		G[i]=_;
	}
	int p2=1;
	while(p2<=m+n) p2<<=1;
	Rev(p2);
	NTT(F,p2,1);
	NTT(G,p2,1);
	for(int i=0;i<p2;i++)
	{
		F[i]*=G[i];
		F[i]%=mod; 
	}
	NTT(F,p2,0);
	for(int i=0;i<=n+m;i++)
	{
		printf("%lld ",F[i]*poww(p2,mod-2)%mod);
	}
}

标签:frac,int,sum,FFT,NTT,笔记,include,omega,2i
From: https://www.cnblogs.com/lizhous/p/17372157.html

相关文章

  • 学习笔记:矩阵快速幂
    1.矩阵乘法设矩阵有\(H\)行,\(L\)列,则两个矩阵\(MatA,MatB\)进行乘法,需要满足\(MatA.L=MatB.H\)。则结果矩阵\(MatR_{i,j}=\sum\limits^{n}_{z=1}MatA_{i,z}*MatB_{z,j}\)。性质:结合律,但不满足交换律。matoperator*(mata,matb){ matc; memset(c.mat,0,sizeof(c.......
  • 树链剖分学习笔记
    一棵树,支持:路径加单点查询一般树上链的问题使用树链剖分解决。重链剖分前置知识LCA,线段树定义重儿子:所有儿子中子树最大的儿子为重儿子重边:重儿子之间的连边重链:若干重儿子连成的链性质一棵树可以被剖成若干重链。优先遍历重儿子,所有重链的dfs序连续。重链数量不......
  • 生成函数学习笔记
    概念序列的母函数(生成函数)是一种形式幂级数。其每一项的系数可以提供关于这个序列的信息,使用母函数解决问题。如:序列\(a\)的生成函数为\(G(x)=\sum\limits_{i=1}^{n}a_if_i(x)\)。其中\(f_i(x)\)是无实际意义的,具体取值看题目要求。但有一些一般取值。一般生成函数令\(f......
  • 拉格朗日插值学习笔记
    拉格朗日插值学习笔记概念拉格朗日插值用于拟合一个函数。可以通过已知函数中的点拟合出函数。若为\(n\)次函数,则需要多于\(n+1\)个点。做法考虑构造\(n+1\)个函数,第\(i\)个函数\(f_i\)对应点\(i\)满足\(f_i(X_i)=Y_i\)且对于其他的点\(j(i\neqj)\)满足\(f_......
  • 学习笔记:数位dp
    1.基本模型数位dp,即以数的每一位作为状态进行dp的算法。通常状态为\(f_{i,0-9}\)表示第\(i\)为取\(0-9\)时的dp值。通常时间复杂度为\(log_{10}n\),十分优秀。2.套路求区间合法类的题,使用容斥思想思想求解,即\([1,r]-[1,l-1]\)dp式子一般很简单,可以使用矩阵快速幂优化......
  • 线性基学习笔记
    概念线性基是一个集合。从原集合中选取任意数都能通过线性基中的数异或得到。本质上是对集合的压缩性质所有数字没有最高位相同的集合大小为\(\log_2\)级别。操作排查:若线性基内有最高位相等的,让其相异或,并继续排查直到没有可操作的数。若原集合内有\(0\)线......
  • 莫队学习笔记
    概念莫队是一种幽雅的暴力。用于处理区间问题。核心思想就是把询问离线下来,然后维护双指针按一定顺序处理每个询问。精髓就在于一定顺序。首先确定一个块长,然后将左端点的位置除以块长,把询问分成若干块。在每个块里按右端点排序。发现当块长为\(\sqrtn\)时两个指针各移动\(......
  • 后缀数组学习笔记
    概念后缀数组,即对于一个串,它的每个后缀按字典序排序后得到的数组。有两个数组要求:\(SA_i\):排名为\(i\)的后缀的开头位置\(RK_i\):以\(i\)为开头的后缀的排名朴素sort排序一下优化倍增优化:我们进行\(\logn\)次排序,第\(k\)次取所有后缀的前\(2^k\)个字符进行......
  • 点分治学习笔记
    概念点分治用于解决有一定要求的链的计数。对于点\(u\)的子树的问题,可以将答案分为:经过点\(u\)不经过点\(u\)第一种可以用桶加暴力。枚举一端的长度,用桶计算另一端长度;第二种分到子树中解决即可。注意到,在随机选根的时候该算法表现不优秀,但若根为重心,因为每次子树......
  • 网络流学习笔记
    概念最大流:在一个网络图上,每个边有流量限制,假如起始点有无线流量,求最多能有多少流量流到终点。增广路:一条从起始点到终点了路径,可以流流量。算法Ford-Fulkerson算法解决这个问题,可以用Ford-Fulkerson算法。该算法的核心就是寻找增广路。每找到一条增广路,就给它它能承受......