封面来源

Pixiv にこまき-44342920

[toc]

前置芝士

1.卷积(其实没啥用)

https://www.bilibili.com/video/BV1JX4y1K7Dr

正文

1.点值表达式

多项式,可以转换为另一种表示方法,即将转为函数

2.多项式乘法

我们知道,两个次多项式相乘的复杂度为,完全不能满足某些题的要求,所以考虑优化。那如果用点值表达式来做乘法呢?

复杂度只有!!!

那么总结一下算法的大体思路:

1.将多项式转为点值表达式

2.计算多项式乘法

3.把结果转回系数表达式

但是转换的过程仍然是,所以还需要优化才行.

3.复数优化

根

定义

若,则nnn$个。

而复数单位根在复平面上模都为,所以这个单位根分布在单位圆上

考虑欧拉公式:

取,则可推出$$ e^{2\pi i}=1=\omega^n$$

此时的单位根称作主次单位根,记作

在下文中,我们记为n次单位根。

性质

单位根有三个性质:

1.对,

证明:

2.对,$$ (\omega_nk)2=\omega_{\frac{n}2}^k $$

证明:

直接展开

3.对,

证明:

等比数列求和公式可得

FFT

DFT:离散傅立叶变换

对多项式,取其在的值,将其转换为点值表达式.这个过程就是DFT。

假设是的幂,即使它不是的幂,我们也可以通过向高次幂补0来添加。

FFT优化DFT

考虑上文中的多项式,重定义为次数为的小多项式,,发现,然后就将大问题化为了两个小问题,即求和,那么,上面那个复杂度为就可以简化为$O(\log n)了。

对于另一边,我们可以通过

得到

。

这个操作叫做蝴蝶变换

IDFT:逆离散傅立叶变换

这个其实就是将FFT再搞一遍。

迭代版FFT

但是,由于上面的过程如果写递归的话会有非常大的常数,所以还要进行一些修改。

FFT分治的过程可以简单地描述一下:

发现其实可以反向迭代的。

1.取出儿子节点,算出DFT

2.用这一步的DFT替换之前的

3.直到迭代到根节点为止,否则重复1

代码

cpp
inline void Init(int lim){
    FOR(i,0,lim)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
}
inline void FFT(node *s,int lim,double flag){
    for(register int i=0;i<lim;i++) if(i<r[i]) swap(s[i],s[r[i]]);
    for(register int i=1;i<lim;i<<=1){
        node f(cos(pi/i),flag*sin(pi/i));
        for(register int j=0;j<lim;j+=(i<<1)){
            node t(1,0);
            for(register int k=0;k<i;k++,t=t*f){
                node nx=s[j+k];
                node ny=t*s[i+j+k];
                s[j+k]=nx+ny;
                s[i+j+k]=nx-ny;
            }
        }
    }
}
 

撒花花~~

★,°:.☆( ̄▽ ̄)/$:.°★ 。