FWT学习笔记

引入

现在有一个长成这样子的卷积:

[C_i=sum_{joplus k=i}A_j imes B_k ]

其中 (oplus) 可以是异或,与,非,序列长度都是 (2^n)

解决

这就需要 FWT 来解决了。

我们之前做 FFT 的时候,是把多项式转化成点值的形式,然后就可以点值相乘了,那么我们也想在 FWT 的过程中实现这个东西,设 (A'[i]) 表示多项式 (A) 转化为点值之后的第 (i) 个点,那么就有:

[C'[i]=A'[i] imes B'[i] ]

我们再设 (c(i,j)) 表示多项式第 (j) 项的系数给第 (i) 个点的贡献,那么就有:

[A'[i]=sum_{j=0}^{2^n-1}A_j ]

[C'[i]=sum_{j=0}^{2^n-1}c(i,j)A_j imessum_{k=0}^{2^n-1}c(i,k)B_k=sum_{j=0}^{2^n-1}sum_{k=0}^{2^n-1}c(i,j)c(i,k)A_jB_k ]

而又有:

[C_p=sum_{t_1oplus t_2=p}A_{t_1} imes B_{t_2} ]

[sum_{p=0}^{2^n-1}c(i,p)C_p=sum_{p=0}^{2^n-1}c(i,p)sum_{t_1oplus t_2=p}A_{t_1} imes B_{t_2} ]

和上面的式子联立就有:

[sum_{j=0}^{2^n-1}sum_{k=0}^{2^n-1}c(i,j)c(i,k)A_jB_k=sum_{p=0}^{2^n-1}c(i,p)sum_{t_1oplus t_2=p}A_{t_1} imes B_{t_2}=sum_{t_1=0}^{2^n-1}sum_{t_2=0}^{2^n-1}c(i,t_1oplus t_2)A_{t_1} imes B_{t_2} ]

所以就有 (c(i,j)c(i,k)=c(i,joplus k)) ,这也是我们要求的 (c) 满足的条件。

然后注意到位运算是可以按位拆开的,那么对于 (c) 也是如此,就有 (c(i,j)=prod_{k=0}^nc(i_k,j_k))

然后考虑知道了 (c) 之后如何转化成点值形式,也就是计算下面这个式子:

[A'[i]=sum_{j=0}^{2^n-1}c(i,j)A_j ]

把这个折半,就有:

[A'[i]=sum_{j=0}^{2^{n-1}-1}c(i,j)A_j+sum_{j=2^{n-1}}^{2^n-1}c(i,j)A_j ]

把它的首位单独拿出来,写 (c(i,j)=c(i_0,j_0)c(i',j')) ,就有:

[A'[i]=c(i_0,0)sum_{j=0}^{2^{n-1}-1}c(i',j')A_j+c(i_0,1)sum_{j=2^{n-1}}^{2^n-1}c(i',j')A_j ]

这样子问题就减半了,可以分治下去。

最后我们需要求 (c) ,这个就变得好求很多了,因为 (c(i,j)) 中的 (i,jin{0,1}) ,然后逆变换对矩阵求逆就可以了。

那么我们写出这个矩阵

[egin{bmatrix}c(0,0) & c(0,1)\c(1,0) & c(1,1)end{bmatrix} ]

(or) 运算为例,我们可以得到关系式:

[c(0,0)=c(0,0)c(0,0) ightarrow c(i,j)in{0,1} ]

[c(0,1)=c(0,0)c(0,1),c(1,1)=c(1,0)c(1,1) ]

通过上面的式子可以计算出好几种矩阵,但还需要满足矩阵有逆,那么得到其中一种矩阵:

[c=egin{bmatrix}1 & 0\1 & 1end{bmatrix},c^{-1}=egin{bmatrix}1 & 0\-1 & 1end{bmatrix} ]

同理,对于 (and) ,就有:

[c=egin{bmatrix}0 & 1\1 & 1end{bmatrix},c^{-1}=egin{bmatrix}-1 & 1\1 & 0end{bmatrix} ]

对于 (xor) ,就有:

[c=egin{bmatrix}1 & -1\1 & 1end{bmatrix},c^{-1}=egin{bmatrix}frac{1}{2} & frac{1}{2}\-frac{1}{2} & frac{1}{2}end{bmatrix} ]

这样子,我们也就可以在 (O(nlog n)) 的时间内解决这个问题了。

例题

  1. 洛谷 P4717 【模板】快速莫比乌斯/沃尔什变换 (FMT/FWT)

就是引入的那道题。

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
const int N = 2e5;
const int p = 998244353;
using namespace std;
int n,a[N + 5],b[N + 5],c[3][3],A[N + 5],B[N + 5];
int mypow(int a,int x){int s = 1;for (;x;x & 1 ? s = 1ll * a * s % p : 0,a = 1ll * a * a % p,x >>= 1);return s;}
void solve(int *a,int n)
{
    for (int i = 1;i < (1 << n);i <<= 1)
        for (int j = 0;j < (1 << n);j += i << 1)
            for (int k = 0;k < i;k++)
            {
                int a0 = a[j + k],a1 = a[j + k + i];
                a[j + k] = (1ll * c[1][1] * a0 % p + 1ll * c[1][2] * a1 % p) % p;
                a[j + k + i] = (1ll * c[2][1] * a0 % p + 1ll * c[2][2] * a1 % p) % p;
            }
}
void solveor()
{
    c[1][2] = 0;
    c[1][1] = c[2][1] = c[2][2] = 1;
    for (int i = 0;i < (1 << n);i++)
        A[i] = a[i],B[i] = b[i];
    solve(A,n);
    solve(B,n);
    for (int i = 0;i < (1 << n);i++)
        A[i] = 1ll * A[i] * B[i] % p;
    c[1][1] = c[2][2] = 1;
    c[2][1] = -1;c[1][2] = 0;
    solve(A,n);
    for (int i = 0;i < (1 << n);i++)
        printf("%d ",(A[i] + p) % p);
    cout<<endl;
}
void solveand()
{
    c[1][1] = 0;
    c[1][2] = c[2][1] = c[2][2] = 1;
    for (int i = 0;i < (1 << n);i++)
        A[i] = a[i],B[i] = b[i];
    solve(A,n);
    solve(B,n);
    for (int i = 0;i < (1 << n);i++)
        A[i] = 1ll * A[i] * B[i] % p;
    c[1][1] = -1;c[2][2] = 0;
    c[2][1] = c[1][2] = 1;    
    solve(A,n);
    for (int i = 0;i < (1 << n);i++)
        printf("%d ",(A[i] + p) % p);
    cout<<endl;
}
void solvexor()
{
    c[1][2] = -1;
    c[2][1] = c[1][1] = c[2][2] = 1;
    for (int i = 0;i < (1 << n);i++)
        A[i] = a[i],B[i] = b[i];
    solve(A,n);
    solve(B,n);
    for (int i = 0;i < (1 << n);i++)
        A[i] = 1ll * A[i] * B[i] % p;
    c[1][1] = c[1][2] = c[2][2] = mypow(2,p - 2);
    c[2][1] = -mypow(2,p - 2);
    solve(A,n);
    for (int i = 0;i < (1 << n);i++)
        printf("%d ",(A[i] + p) % p);
    cout<<endl;
}
int main()
{
    scanf("%d",&n);
    for (int i = 0;i < (1 << n);i++)
        scanf("%d",&a[i]);
    for (int i = 0;i < (1 << n);i++)
        scanf("%d",&b[i]);
    solveor();
    solveand();
    solvexor();
    return 0;
}
  1. 洛谷 P3175 [HAOI2015]按位或
  1. CF449D Jzzhu and Numbers
  1. CF1119H Triple
原文地址:https://www.cnblogs.com/sdlang/p/14292493.html