FFT 快速傅里叶变换
前置知识 n阶多项式可以用n+1个点表示(可用线性代数证明)
所以 正常两个n阶多项式相乘 复杂度应该是o(N²)
而将多项式点化(值表达式) 即H(x)=G(x)*F(x)
只需要在G多项式与F多项式找到相对应的点2x+1个 然后相乘 便得到了H 复杂度为o(N)
现在的问题在于 我们如何将多项式与值表达式进行互换
这里就需要FFT来发挥作用
假设我们现在有一个数组a 要求他任意两个元素的和或差 朴素算法为n² 但将这个数组看为多项式呢?;
将a的元素看为次数 比方说有一数组a{1 3 4 5} 则有 0+x+0+x³+x4+x5;
现在要求任意两个元素的和 和她本身相乘 得到的多项式的系数就是出现的次数
减法同理 但需要做正数处理(+一个最大值)
例https://ac.nowcoder.com/acm/contest/11166/H
快速傅里叶变换还可以用于求k阶前缀和 将原数组与一列1相乘即可
例https://ac.nowcoder.com/acm/contest/19483/C
void FFT(Complex *a,int inv){ for(int i=0;i<limit;i++) if(i<rev[i])swap(a[i],a[rev[i]]); for(int mid=1;mid<limit;mid<<=1){ Complex Wn=Complex({cos(PI/mid),inv*sin(PI/mid)}); for(int i=0;i<limit;i+=mid*2){ Complex w=Complex({1,0}); for(int j=0;j<mid;j++,w=w*Wn){ Complex x=a[i+j],y=w*a[i+j+mid]; a[i+j]=x+y,a[i+j+mid]=x-y; } } } if(inv==-1) for(int i=0;i<limit;i++) A[i].x/=limit; }
int
bit=20;
limit=1<<20;
//limit要大于最高次数
for
(
int
i=1;i<limit;i++) rev[i]=rev[i>>1]>>1|(i&1)<<(bit-1);//用于分治
FFT(A,1),FFT(B,1); for(int i=0;i<limit;i++) A[i]=A[i]*B[i]; FFT(A,-1); for(int i=0;i<=Bs;i++) vis[i]=int(A[i+Bs].x+0.5);//表示系数,因为是减法,加过Bs,大于Bs的符合要求,进行判断是否存在改次方 //多项式A与B相乘 乘完之后-1做逆运算回到系数表示法