FFT(快速傅里叶变换)

题目链接

3122. 多项式乘法P3803 【模板】多项式乘法(FFT)

3122. 多项式乘法

题目描述

给定一个 n 次多项式 F(x)=a0+a1x+a2x2++anxn

以及一个 m 次多项式 G(x)=b0+b1x+b2x2++bmxm

已知 H(x)=F(x)G(x)=c0+c1x+c2x2++cn+mxn+m

请你计算并输出 c0,c1,,cn+m

输入格式

第一行包含两个整数 n,m

第二行包含 n+1 个整数 a0,a1,,an

第三行包含 m+1 个整数 b0,b1,,bm

输出格式

共一行,依次输出 c0,c1,,cn+m

数据范围

1n,m105,
0ai,bi9

输入样例:

1 2 1 3 2 2 1

输出样例:

2 8 7 3

解题思路

fft

一个 n1 次多项式 f(x)=a0+a1×x1+a2×x2++an1×xn1可以由 n(xi,f(xi)) 点唯一表示

证明:
即求解 n 元一次方程的 ai,将其系数用行列式表示出来:

|1x1x1n11x2x2n11xnxnn1|

即该系数行列式即为范德蒙行列式的转置,故其值为:1j<in(xixj),而 xixj,故其值不为 0,而一个一次 n 元方程组有唯一解则其系数行列式不为 0,故其解唯一,所以一个 n1 次多项式可以由 n 个点唯一表示

一般 fft 都是用来求解多项式的卷积(乘积),暴力做法的复杂度为 O(nm),而 fft 利用上述性质,即将其转化为点表示法,利用点表示法求出结果的点表示法,最后再将点表示法转化为系数表示法,例如:f(x)=g(x)×h(x),将 g(xi)h(xi) 转化为点表示法,f(x)n+m 项,g(x)h(x) 不足的补零,将对应的共同的 xig(xi)h(xi) 相乘,得 f(x) 的点表示法,最后再将点表示法转化为系数表示法,而点表示法和系数表示法的相互转化的复杂度为 O(nlogn),即 fft 的复杂度为 O((n+m)×log(n+m))

所以 fft 的关键在于点表示法和系数表示法的相互转化
系数表示法转化为点表示法
其中点当然也可以是复数,故这 n 个点可以为单位圆上辐角均分的 n 个点,用 wnk 表示为第 k 个点,其表示的复数为 (cos(2π×kn),sin(2π×kn)),另外要用到的复数乘法的一些性质:乘法后的复数模长为两复数模长相乘,辐角为两复数辐角相加,则有 wnk+n2=wnkw2n2k=wnk
设一个 n1n2 次幂)次多项式 A(x)=a0+a1x+a2x2++an2xn2+an1xn1
设:

A1(x)=a0+a2x2++an2xn21A2(x)=a1+a3x1++an1xn21

则有 A(x)=A1(x2)+xA2(x2)
假设 0k<n2 ,将 x=ωnk 代入,得

A(ωnk)=A1(ωn2k)+ωnkA2(ωn2k)

x=ωnk+n2 代入,得

A(ωnk+n2)=A1(ωn2k+n)+ωnk+n2A2(ωn2k+n)=A1(ωn2k)+ωnk+n2A2(ωn2k)=A1(ωn2k)ωnkA2(ωn2k)

即如果知道一半的 A(ωnk),则可以知道另一半的 A(ωnk+n2),另外每次都能将原问题的求解规模缩小一半,故其时间复杂度为 O(nlogn)
由于 fft 的递归写法常数巨大,故一般采用迭代写法,例如:

a0 a1 a2 a3 a4 a5 a6 a7_a0 a2 a4 a6_  a1 a3 a5 a7_a0 a4_  a2 a6_  a1 a5_  a3 a7_a0_ a4_ a2_ a6_ a1_ a5_ a3_ a7_

其中最上层的 ai 表示按多项式顺序的值,最下层表示初始时的 ai,例如 a0 由下层的 a0a1 计算得到,其他同理。迭代需要最底层的数,下标的二进制之间互为翻转关系(蝴蝶变换)。简略证明:如果一个数为奇数,即二进制下最低位为 1,则其一定在另外一半中出现,而另外一半最高位都为 1,即最高位和最低位满足翻转关系,同理不考虑最高位和最低位的情况下其他位也满足翻转关系。得到最底层,往上计算即得点表示法,具体计算:设置一个变量 mid,表示当前区间数需要计算的次数,由于每次只需计算一半,2×mid 即当前区间数,i 为每个区间开始的位置,j 为每个区间的位置变量,i+j 即为每个区间中多项式对应的值,据此递推计算即可

点表示法转化为系数表示法

设一个 n1n2 次幂)次多项式 A(x)=a0+a1x+a2x2++an2xn2+an1xn1,由迭代过程得到 n 个多项式的值 yi,则 ak=i=0n1yi(ωnk)in

证明:i=0n1yi(ωnk)i=i=0n1j=0n1aj(ωni)j(ωnk)i=j=0n1aji=0n1(ωnjk)i
s(x)=1+x+x2++xn1,则如果 k0s(ωnk)=1+ωnk+ωn2k++ωn(n1)k,而 ωnks(ωnk)=ωnk+ωn2k++ωnn=s(ωnk),而 ωnk0,则 s(ωnk)=0,如果 k=0,则 s(ωnk)=n。故 j=0n1aji=0n1(ωnjk)i 当且仅当 j=ki=0n1(ωnjk)i0,且其等于 n,故 j=0n1aji=0n1(ωnjk)i=nak,得证
g(x)=i=0n1yixi,则 ak=g(ωnk)n,即可以利用转化为点表示法的过程求解系数表示法

  • 时间复杂度:O((n+m)×log(n+m))

代码

// Problem: 多项式乘法 // Contest: AcWing // URL: https://www.acwing.com/problem/content/3125/ // Memory Limit: 64 MB // Time Limit: 1000 ms // // Powered by CP Editor (https://cpeditor.org) // %%%Skyqwq #include <bits/stdc++.h> //#define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; typedef pair<LL, LL> PLL; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } const double pi=acos(-1); const int N=3e5+5; int n,m,bit,tot,rev[N]; struct cp { double x,y; cp operator+(const cp &o)const { return {x+o.x,y+o.y}; } cp operator-(const cp &o)const { return {x-o.x,y-o.y}; } cp operator*(const cp &o)const { return {x*o.x-y*o.y,x*o.y+y*o.x}; } }a[N],b[N]; void fft(cp a[],int inv) { for(int i=0;i<tot;i++) if(i<rev[i])swap(a[i],a[rev[i]]); for(int mid=1;mid<tot;mid<<=1) { cp w1={cos(pi/mid),inv*sin(pi/mid)}; for(int i=0;i<tot;i+=mid*2) { cp wk={1,0}; for(int j=0;j<mid;j++,wk=wk*w1) { cp x=a[i+j],y=a[i+j+mid]; a[i+j]=x+wk*y,a[i+j+mid]=x-wk*y; } } } } int main() { help; cin>>n>>m; for(int i=0;i<=n;i++)cin>>a[i].x; for(int i=0;i<=m;i++)cin>>b[i].x; while((1<<bit)<n+m+1)bit++; tot=1<<bit; for(int i=0;i<tot;i++)rev[i]=rev[i>>1]>>1|((i&1)<<(bit-1)); fft(a,1),fft(b,1); for(int i=0;i<tot;i++)a[i]=a[i]*b[i]; fft(a,-1); for(int i=0;i<=n+m;i++)cout<<int(a[i].x/tot+0.5)<<' '; return 0; }

__EOF__

本文作者acwing_zyy
本文链接https://www.cnblogs.com/zyyun/p/15458728.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zyy2001  阅读(434)  评论(0编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示