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