FWT 笔记

给定长度为 \(2^n\) 两个序列 \(A,B\),设

\[C_i=\sum_{j\textup{ op }k = i}A_j \times B_k \]

分别当 \(\textup{op}\) 是 or, and, xor 时求出 \(C\)

FWT 就是解决所谓位运算卷积问题的。

我们用 \(C=A\cdot B\) 表示 \(C_k=A_kB_k\)

构造一个 \(\textup{FWT}(A),\textup{FWT}(B)\),使得 \(\textup{FWT}(C)=\textup{FWT}(A)\cdot\textup{FWT}(B)\),从而 \(C=\textup{FMT}^{-1}(\textup{FMT}(A)\cdot\textup{FWT}(B))\)

我们考虑构造形如 \(\textup{FWT}(A)_i=\sum_{j}w(i,j)A_j\) 的东西。

那么就有

\[\sum_{i}w(k,i)A_i\sum_j w(k,j)B_j=\sum_{i,j}w(k,i)w(k,j)A_iB_j=\sum_{\mathscr{l}}w(k,\mathscr{l})\sum_{i\textup{ op }j=\mathscr{l}}A_iB_j=\sum_{i,j}w(k,i\textup{ op }j)A_iB_j \]

于是

\[w(k,i)w(k,j)=w(k,i\operatorname{ op }j) \]

好的,凭借某些惊人的直觉,便可以猜出其分别为

\[w(i,j)=[i\operatorname{or}j=i] \]

\[w(i,j)=[i\operatorname{and}j=i] \]

\[w(i,j)=(-1)^{\operatorname{popcount}(i\operatorname{and}j)} \]

不难验证其正确性。

如何求?

这很类似于 FFT,所以我们考虑分治求解

现在,从低到高枚举每一位,每一段内的 FWT 已经被求解,假设现在是 \(k\)

OR

我们考虑每一个形如 \(i2^k+2^{k-1}+j\) 的位置,就有

\[A_{i2^k+2^{k-1}+j}\leftarrow A_{i2^k+2^{k-1}+j}+A_{i2^k+j} \]

可能不是很好理解,所以我们来看代码:

template<typename T>
void FWTor(int n,T *f){
    n=(1<<n);
    for(int o=2,k=1;o<=n;o<<=1,k<<=1)
        for(int i=0;i<n;i+=o)
            for(int j=0;j<k;++j)
                f[i+j+k]=f[i+j+k]+f[i+j];
}

仔细观察就会发现这本质和高维前缀和是一致的。

AND

我们考虑每一个形如 \(i2^k+j\) 的位置,就有

\[A_{i2^k+j}\leftarrow A_{i2^k+2^{k-1}+j}+A_{i2^k+j} \]

template<typename T>
void FWTand(int n,T *f){
    n=(1<<n);
    for(int o=2,k=1;o<=n;o<<=1,k<<=1)
        for(int i=0;i<n;i+=o)
            for(int j=0;j<k;++j)
                f[i+j]=f[i+j+k]+f[i+j];
}

仔细观察就会发现这本质和高维后缀和是一致的。

XOR

我们考虑每一个形如 \(i2^k+j\)\(i2^k+2^{k-1}+j\) 的位置,就有(为了区分,我们用 \(A^{\prime}\) 表示之后的数组)

\[A^{\prime}_{i2^k+j}\leftarrow A_{i2^k+j}+A_{i2^k+2^{k-1}+j} \]

\[A^{\prime}_{i2^k+2^{k-1}+j}\leftarrow A_{i2^k+j}-A_{i2^k+2^{k-1}+j} \]

template<typename T>
void FWTxor(int n,T *f){
    n=(1<<n);
    T tmp;
    for(int o=2,k=1;o<=n;o<<=1,k<<=1)
        for(int i=0;i<n;i+=o)
            for(int j=0;j<k;++j){
                tmp=f[i+j];
                f[i+j]=f[i+j+k]+f[i+j];
                f[i+j+k]=tmp-f[i+j+k];
            }
}

考虑怎么反过来?

这里我们直接解方程组,就可以有

某些理解中把这视为矩阵求逆,其实就是这意思。

IOR

我们考虑每一个形如 \(i2^k+2^{k-1}+j\) 的位置,就有

\[A_{i2^k+2^{k-1}+j}\leftarrow A_{i2^k+2^{k-1}+j}-A_{i2^k+j} \]

可以和之前代码合并

template<typename T>
void FWTor(int n,T *f,int type){
    n=(1<<n);
    for(int o=2,k=1;o<=n;o<<=1,k<<=1)
        for(int i=0;i<n;i+=o)
            for(int j=0;j<k;++j)
                f[i+j+k]=f[i+j+k]+type*f[i+j];
}

仔细观察就会发现这本质和高维差分是一致的。

IAND

我们考虑每一个形如 \(i2^k+2^{k-1}+j\) 的位置,就有

\[A_{i2^k+2^{k-1}+j}\leftarrow A_{i2^k+2^{k-1}+j}-A_{i2^k+j} \]

可以和之前代码合并

template<typename T>
void FWTand(int n,T *f,int type){
    n=(1<<n);
    for(int o=2,k=1;o<=n;o<<=1,k<<=1)
        for(int i=0;i<n;i+=o)
            for(int j=0;j<k;++j)
                f[i+j]=f[i+j]+type*f[i+j+k];
}

仔细观察就会发现这本质和高维差分是一致的。

IXOR

我们考虑每一个形如 \(i2^k+j\)\(i2^k+2^{k-1}+j\) 的位置,就有(为了区分,我们用 \(A^{\prime}\) 表示之后的数组)

\[A^{\prime}_{i2^k+j}\leftarrow \dfrac{1}{2}(A_{i2^k+j}+A_{i2^k+2^{k-1}+j}) \]

\[A^{\prime}_{i2^k+2^{k-1}+j}\leftarrow \dfrac{1}{2}(A_{i2^k+j}-A_{i2^k+2^{k-1}+j}) \]

template<typename T>
void FWTxor(int n,T *f,int type){
    n=(1<<n);
    T tmp;
    for(int o=2,k=1;o<=n;o<<=1,k<<=1)
        for(int i=0;i<n;i+=o)
            for(int j=0;j<k;++j){
                tmp=f[i+j];
                f[i+j]=f[i+j+k]+f[i+j];
                f[i+j+k]=tmp-f[i+j+k];
                f[i+j]=f[i+j]*type,f[i+j+k]=f[i+j+k]*type;
            }
}

全部使用 CV 大法完成。

恭喜你已经会了 FWT!

posted @ 2025-10-22 10:20  ThisIsLu  阅读(23)  评论(0)    收藏  举报