• F F T FFT的作用 FFT

O ( l o g N ) O(logN) O(logN) 计算两个多项式的卷积 .

  • F F T FFT的前置 FFT

  1. 单位根

. , . 单位根为在单位圆上的点.\\ 横坐标为实部, 纵坐标为虚部的复数 . .,.


如图, 图中落在 x x x 正半轴的那个红点称为 w n 0 w_{n}^0 wn0, 被称为 n n n 次单位根, 其余的点从 w n 0 w_n^0 wn0 逆时针顺序分别称为 w n 1 , w n 2 . . . w n 7 w_n^1, w_n^2...w_n^7 wn1,wn2...wn7 .

从图中可以 感性 得到 单位根 2 2 2 个性质 \downarrow

  • 1 : 性质1: 1: w 2 n 2 k = w n k w_{2n}^{2k}=w_n^k w2n2k=wnk

  • 2 : 性质2: 2: w n k + n 2 = w n k w_n^{k+\frac{n}{2}} = -w_n^k wnk+2n=wnk


下面给出 理性 证明, 首先要知道

: w n k = c o s ( k 2 π n ) + i s i n ( k 2 π n ) 欧拉公式:w_n^k=cos(k*\frac{2 \pi}{n}) + i*sin(k*\frac{2 \pi}{n}) :wnk=cos(kn2π)+isin(kn2π)

其中 i i i 1 \sqrt{-1} 1 .


  • 1 : 性质1: 1: w 2 n 2 k = w n k w_{2n}^{2k}=w_n^k w2n2k=wnk
    : 证: :
    w 2 n 2 k = c o s ( 2 k 2 π 2 n ) + i s i n ( 2 k 2 π 2 n ) = w n k w_{2n}^{2k} = cos(2k*\frac{2 \pi}{2n}) + i*sin(2k*\frac{2 \pi}{2n})=w_n^k w2n2k=cos(2k2n2π)+isin(2k2n2π)=wnk

  • 2 : 性质2: 2: w n k + n 2 = w n k w_n^{k+\frac{n}{2}} = -w_n^k wnk+2n=wnk
    : 证: :
    w n k + n 2 = c o s [ ( k + n 2 ) 2 π n ] + i s i n [ ( k + n 2 ) 2 π n ) ] = w n k w_n^{k+\frac{n}{2}}\\ =cos[(k+\frac{n}{2})*\frac{2\pi}{n}]+i*sin[(k+\frac{n}{2})*\frac{2\pi}{n})]\\ =-w_n^k wnk+2n=cos[(k+2n)n2π]+isin[(k+2n)n2π)]=wnk

  • F F T FFT的内容 FFT

  • 有多项式 A ( x ) = a 0 + a 1 x + a 2 x 2 + ​ + a n 1 x n 1 A(x)=a_0 + a_1x+a_2x^2+\dotsi+a_{n-1}x^{n-1} A(x)=a0+a1x+a2x2++an1xn1

n n + 1 . \because 一个n次多项式可以被n+1个点唯一确定. nn+1.
w n 0 ​ w n n 1 . \therefore 将 w_n^0 \dotsi w_n^{n-1} 代入可以确定该多项式. wn0wnn1.

这里默认 n n n 2 2 2 的整数次幂, 所以 n 1 n-1 n1 为奇数 .

  • 对于上方多项式 A ( x ) A(x) A(x), 将其 系数 按奇偶分开, 得
    A ( x ) = ( a 0 + a 2 x 2 + ​ + a n 2 x n 2 ) + ( a 1 x + a 3 x 3 + ​ + a n 1 x n 1 ) A(x) = (a_0+a_2*x^2+ \dotsi + a_{n-2}*x^{n-2})+(a_1*x+a_3*x^3+ \dotsi + a_{n-1}*x^{n-1}) A(x)=(a0+a2x2++an2xn2)+(a1x+a3x3++an1xn1)

    A 1 ( x ) = a 0 + a 2 x + a 4 x 2 + &NegativeThinSpace; + a n 2 x n 2 1 <mtext>   </mtext> A 2 ( x ) = a 1 + a 3 x + a 5 x 2 + &NegativeThinSpace; + a n 1 x n 2 1 A_1(x)=a_0+a_2x+a_4x^2+ \dotsi+a_{n-2}x^{\frac{n}{2}-1}\\ \ \\ A_2(x)=a_1+a_3x+a_5x^2+ \dotsi+a_{n-1}x^{\frac{n}{2}-1} A1(x)=a0+a2x+a4x2++an2x2n1 A2(x)=a1+a3x+a5x2++an1x2n1

    A ( x ) = A 1 ( x 2 ) + x A 2 ( x 2 ) A(x)=A_1(x^2)+x*A_2(x^2) A(x)=A1(x2)+xA2(x2)

<mtext>   </mtext> w n k <mtext>      </mtext> ( k &lt; n 2 ) <mtext>   </mtext> 代入\ w_n^k\ \ \ \ (k &lt; \frac{n}{2})\ 得  wnk    (k<2n) 
A ( w n k ) = A 1 ( w n 2 k ) + w n k A 2 ( w n 2 k ) = A 1 ( w n 2 k ) + w n k A 2 ( w n 2 k ) A(w_n^k) = A_1(w_n^{2k})+w_n^kA_2(w_n^{2k})=A_1(w_{\frac{n}{2}}^k)+w_n^kA_2(w_{\frac{n}{2}}^k) A(wnk)=A1(wn2k)+wnkA2(wn2k)=A1(w2nk)+wnkA2(w2nk)

<mtext>   </mtext> w n k + n 2 <mtext>   </mtext> 代入\ w_n^{k+\frac{n}{2}}\ 得  wnk+2n 
A ( w n k + n 2 ) <mtext>   </mtext> = A 1 ( w n 2 k + n ) + w n k + n 2 A 2 ( w n 2 k + n ) <mtext>   </mtext> = A 1 ( w n 2 k w n n ) w n k A 2 ( w n 2 k w n n ) <mtext>   </mtext> = A 1 ( w n 2 k ) w n k A 2 ( w n 2 k ) <mtext>   </mtext> = A 1 ( w n 2 k ) w n k A 2 ( w n 2 k ) A(w_n^{k+\frac{n}{2}})\\\ \\=A_1(w_n^{2k+n})+w_n^{k+\frac{n}{2}}A_2(w_n^{2k+n})\\ \ \\ =A_1(w_n^{2k}*w_n^n)-w_n^kA_2(w_n^{2k}w_n^n)\\ \ \\ =A_1(w_n^{2k})-w_n^kA_2(w_n^{2k}) \\ \ \\ =A_1(w_{\frac{n}{2}}^k)-w_n^kA_2(w_{\frac{n}{2}}^k) A(wnk+2n) =A1(wn2k+n)+wnk+2nA2(wn2k+n) =A1(wn2kwnn)wnkA2(wn2kwnn) =A1(wn2k)wnkA2(wn2k) =A1(w2nk)wnkA2(w2nk)

: 结论: :
  • A ( w n k ) = A 1 ( w n 2 k ) + w n k A 2 ( w n 2 k ) A(w_n^k) = A_1(w_{\frac{n}{2}}^k)+w_n^kA_2(w_{\frac{n}{2}}^k) A(wnk)=A1(w2nk)+wnkA2(w2nk)

  • A ( w n k + n 2 ) = A 1 ( w n 2 k ) w n k A 2 ( w n 2 k ) A(w_n^{k+\frac{n}{2}})=A_1(w_{\frac{n}{2}}^k)-w_n^kA_2(w_{\frac{n}{2}}^k) A(wnk+2n)=A1(w2nk)wnkA2(w2nk)

可以观察到第二个式子与第一个式子的唯一区别就是正负号 .

根据上方式子分治处理出每个子项, 再向上合并, 时间复杂度 O ( N l o g N ) O(NlogN) O(NlogN) .


: 总结: :

F F T FFT FFT是先将多项式转换为 <stron>, 然后进行分治处理, 再转变为多项式的 O ( n l o g n ) O(nlogn) O(nlogn) 算法 .</stron>

推导过程:

  1. 把系数按奇偶分开
  2. 代入 单位根 .
  3. 得到 “分治向上递推式” .

  • F F T FFT的实现 FFT

  • 蝴蝶变换

借用 这位大佬 的一个图,

所谓蝴蝶变换, 就是将原序列进行二进制翻转的变换 .

r e v [ i ] = ( r e v [ i &gt; &gt; 1 ] &gt; &gt; 1 ) ( ( i &amp; 1 ) &lt; &lt; b i t _ n u m 1 ) rev[i] = (rev[i&gt;&gt;1]&gt;&gt;1) | ((i\&amp;1) &lt;&lt; bit\_num-1) rev[i]=(rev[i>>1]>>1)((i&1)<<bit_num1)

详解点击 这里 .


下面是 模板 代码 .

#include<bits/stdc++.h>

#define reg register

const int maxn = 1e6 + 5;
const double Pi = acos(-1);

struct complex{
        double x, y;
        complex(double x = 0, double y = 0):x(x), y(y) {}
} A[maxn<<2], B[maxn<<2];

complex operator + (complex a, complex b){ return complex(a.x+b.x, a.y+b.y); }
complex operator - (complex a, complex b){ return complex(a.x-b.x, a.y-b.y); }
complex operator * (complex a, complex b){ return complex(a.x*b.x-a.y*b.y, a.x*b.y+a.y*b.x); }

int N, M;
int rev[maxn<<2];

void FFT(complex *F, short Opt){
        for(int i = 0; i < N; i ++)
                if(i < rev[i]) std::swap(F[i], F[rev[i]]);
        for(int p = 2; p <= N; p <<= 1){
                int len = p >> 1;
                complex tmp(cos(Pi/len), Opt*sin(Pi/len));
                for(int i = 0; i < N; i += p){
                        complex Buf(1, 0);
                        for(reg int k = i; k < i+len; k ++){
                                complex Temp = Buf * F[k+len];
                                F[k+len] = F[k] - Temp;
                                F[k] = F[k] + Temp;
                                Buf = Buf * tmp;
                        }
                }
        }
}

int main(){
        N = read(), M = read();
        for(int i = 0; i <= N; i ++) A[i].x = read();
        for(int i = 0; i <= M; i ++) B[i].x = read();
        int bit_num = 0;
        for(M += N, N = 1; N <= M; N <<= 1) bit_num ++;
        for(int i = 0; i < N; i ++) rev[i] = (rev[i>>1]>>1)|((i&1)?N>>1:0);
        // for(int i = 0; i < N; i ++) rev[i] = (rev[i>>1]>>1) | ((i&1) << bit_num-1);
        FFT(A, 1), FFT(B, 1);
        for(int i = 0; i < N; i ++) B[i] = A[i] * B[i];
        FFT(B, -1);
        for(reg int i = 0; i <= M; i ++) printf("%.0lf ", fabs(B[i].x/N));
        return 0;
}
  • F F T FFT的应用 FFT

万径人踪灭

礼物


  • F F T <mtext>   </mtext> &gt; N T T FFT\ 魔改-&gt; NTT FFT >NTT

  1. 将单位根 w n w_n wn 替换为原根 g m o d 1 l e n g^{\frac{mod-1}{len}} glenmod1
  2. 最后乘的是 T _ l e n T\_len T_len 的逆元 .

其中 g g g 在 模数 998244353 998244353 998244353下取 3 3 3 .

void FFT(complex *F, int opt){
        for(reg int i = 0; i < FFT_Len; i ++)
                if(i < rev[i]) std::swap(F[i], F[rev[i]]);
        for(reg int p = 2; p <= FFT_Len; p <<= 1){
                int half = p >> 1;
                complex t = complex(cos(Pi/half), opt*sin(Pi/half));
                for(reg int i = 0; i < FFT_Len; i += p){
                        complex buf = complex(1, 0);
                        for(reg int k = i; k < i+half; k ++){
                                complex Tmp = buf * F[k + half];
                                F[k + half] = F[k] - Tmp;
                                F[k] = F[k] + Tmp;
                                buf = buf * t;
                        }
                }
        }
}

void NTT(int *f, int opt){
        for(reg int i = 0; i < T_len; i ++)
                if(i < rev[i]) std::swap(f[i], f[rev[i]]);
        for(reg int p = 2; p <= T_len; p <<= 1){
                int half = p >> 1;
                int wn = Ksm(3, (mod-1)/p);
                if(opt == -1) wn = Ksm(wn, mod-2);
                for(reg int i = 0; i < T_len ; i += p){
                        int buf = 1;
                        for(reg int k = i; k < i+half; k ++){
                                int Tmp_1 = 1ll*buf*f[k+half] % mod;
                                f[k+half] = (f[k] - Tmp_1 + mod) % mod;
                                f[k] = (f[k] + Tmp_1) % mod;
                                buf = 1ll*buf*wn % mod;
                        }
                }
        }
}