从 FWT 到 FFT

与同学讨论之后顿悟了如何从 FWT 得到 FFT,神乎其神。

对于二进制异或卷积,我们设置 \(n\) 个变元 \(x_{0\sim n-1}\),令 \(X^S=\prod\limits_{i\in S}x_i\),做 \(n\) 元多项式乘法,根据二进制异或的运算表 \((1,1)\to0\),得到这样做的充要条件 \(x_i^2=1\),即在模 \((x_i^2-1)\) 意义下做多项式乘法。对 \((x_i^2-1)\) 因式分解得到 \((x_i+1)(x_i-1)\),根据群论知识,相当于对 \(2^n\) 个点值代入后点乘,最后插值出来。

扩展到三进制,依然设置 \(n\) 个变元,每一位可能取到 \(x_i^0,x_i^1,x_i^2\),要求 \(x_i^3=1\)。上三次单位根进行因式分解,得到 \((x_i-\omega_3^0)(x_i-\omega_3^1)(x_i-\omega_3^2)\),将 \(3^n\) 个点值代入后点乘。作为例子,写一下转移矩阵:

\[\begin{pmatrix} 1&1&1\\ 1&\omega&\omega^2\\ 1&\omega^2&\omega^4 \end{pmatrix} \]

于是我们得到 \(k\) 进制异或卷积的理论方法,利用 \(k\) 次单位根插值即可。每一位都是独立的,容易设计出 \(O(nk^{n+1})\) 的做法。具体来说就是每位存在一个转移矩阵,乘上这个矩阵进行转移即可。

好的,上面实际上讲述了多维 \(k\) 项式循环卷积的一般方法,我们尝试推广到一般多项式乘法。对于两个 \(n\) 次多项式相乘,我们可以认为其就是 \(N\) 项式循环卷积,其中 \(N>2n\),这个卷积是一维的,直接类比上文,得到 \(x^N=1\),使用 \(N\) 次单位根处理,但是直接矩阵乘向量是 \(O(N^2)\) 的。可以分治做到 \(O(N\log N)\),那是纯粹的多项式技巧。

本文揭示了 FWT 和 FFT 之间的深刻联系,真是一篇不可多得的好文!


实则模拟赛碰到七进制 FWT 根本写不出来:

int b[117649][7],lim,Ma[7][7],Mb[7][7],A[7],B[7];
void init(){
    vint pw(7);
    int v=ksm(3,(mod-1)/7);
    pw[0]=1;
    for(int i=1;i<7;i++)pw[i]=pw[i-1]*v%mod;
    for(int i=0;i<7;i++)
        for(int j=0;j<7;j++)Ma[i][j]=pw[i*j%7];
    v=ksm(7);
    for(int i=0;i<7;i++)
        for(int j=0;j<7;j++)Mb[i][j]=pw[(7-i*j%7)%7]*v%mod;
}
struct sps{
    int a[117649];
    sps(){memset(a,0,sizeof(a));}
    void FWT(){
        for(int i=0;i<m;i++){
            int o=ksm(7,i);
            for(int j=0;j<lim;j++)
                if(b[j][i]==0){
                    int now=j;
                    for(int k=0;k<7;k++)B[k]=a[now],A[k]=0,now+=o;
                    for(int k=0;k<7;k++)
                        for(int p=0;p<7;p++)inc(A[k],Ma[k][p]*B[p]%mod);
                    now=j;
                    for(int k=0;k<7;k++)a[now]=A[k],now+=o;
                }
            }
    }
    void IFWT(){
        for(int i=0;i<m;i++){
            int o=ksm(7,i);
            for(int j=0;j<lim;j++)
                if(b[j][i]==0){ 
                    int now=j;
                    for(int k=0;k<7;k++)B[k]=a[now],A[k]=0,now+=o;
                    for(int k=0;k<7;k++)
                        for(int p=0;p<7;p++)inc(A[k],Mb[k][p]*B[p]%mod);
                    now=j;
                    for(int k=0;k<7;k++)a[now]=A[k],now+=o;
                }
            }
    }
};
posted @ 2026-03-03 15:36  BYR_KKK  阅读(132)  评论(0)    收藏  举报