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做逆运算回到系数表示法

 

posted @ 2021-07-18 16:32  旅玖旅玖  阅读(50)  评论(0编辑  收藏  举报