FWT

Posted acmsn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FWT相关的知识,希望对你有一定的参考价值。

\\(FFT\\)(快速傅里叶变换)求的是卷积,也就是
\\[ C_k=\\sum_i+j=kA_iB_j \\]
那么\\(FWT\\)(快速沃尔什变换)求的就是子集卷积,也就是
\\[ C_k=\\sum_i \\oplus j=k A_i B_j \\]
\\(\\oplus\\)指按位运算\\(or,and,xor\\)

其实\\(or,and\\)卷积应该是\\(FMT\\)(快速莫比乌斯变换),因为思想较为类似,故写在一篇博客中。

\\(FMT\\)

先介绍\\(or\\)卷积,因为感性上理解\\(or\\)\\(and\\)操作较为类似,所以两者实际是等价的。

根据\\(FFT\\)的思路,我们同样需要变换出一个函数使得
\\[ FMT(A)_i\\times FMT(B)_i=FMT(C)_i \\]
然后莫比乌斯变换就是构造了下面这个式子:
\\[ FMT(A)_n=\\sum _i \\subseteq nA_i \\]
证明也很简单:
\\[ \\sum_i \\subseteq n A_i \\sum_j \\subseteq n B_j=\\sum_i, j \\subseteq n A_i B_j=\\sum_k \\subseteq n \\sum_i | j=k A_i B_j \\]
注意到
\\[ C_k=\\sum_i\\oplus j = kA_iB_j \\]
那么
\\[ FMT(A)_n\\times FMT(b)_n=\\sum_k\\subseteq n=FMT(C)_n \\]
于是我们需要快速地求出子集和。

有两种主要的方法,一种是高维前缀和,可以看这里

另一种是分治的算法,借一张网上的图,箭头表示累加,应该就能理解了。

技术图片

变回去是把累加改为减就可以了。

代码如下:

void FMT(int *a, int n, int x)

    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)
                a[i + j + k] += a[j + k] * x;

\\(and\\)卷积也很类似
\\[ FMT(A)_n=\\sum_n\\subseteq iA_i \\]
代码如下:

void FMT(int *a, int n, int x)

    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)
                a[j + k] += a[i + j + k] * x;

\\(FWT\\)

未完待续。。。

参考文献:《真正理解快速沃尔什变换/快速莫比乌斯变换(FWT|FMT)》

以上是关于FWT的主要内容,如果未能解决你的问题,请参考以下文章

模板快速沃尔什变换

CF 914 G Sum the Fibonacci —— 子集卷积,FWT

Codeforces 1016G Appropriate Team 数论 FWT

bzoj 4589 Hard Nim —— FWT

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

FWT,FST入门