什么是FWT?
我们知道普通的卷积是这样的:
Ci=∑j+k=iAj∗Bk
我们用FFT可以加速这一过程。
现在我们把 + 改成某位运算,成了位运算的卷积:
FWT就是用于解决这种卷积的。
具体思想
考虑一个变换:
DWT(A)i=∑j=0n−1Aj∗f(i,j)
如果我们能找到这样一个变化系数 f(i,j) ,使得
DWT(A)i∗DWT(B)i=DWT(C)i(C是A与B的位运算卷积) ,
就能类似FFT一样,先转换,再乘,再逆转换。关键在 f(i,j) 如何构造。
带入一下,上面那个条件其实就等价于: f(i,j)∗f(i,k)=f(i,j⊕k) 。
直接给出构造:
⊕为与:
f(i,j)=[i&j=i]
⊕为或:
f(i,j)=[i&j=j]
⊕为异或:
f(i,j)=(−1)cnt(i&j)
(count(x)表示x二进制下1的个数)
并不知道是怎么来的,但是容易验证是对的。
这些构造都有一个重要的性质,二进制每位都是独立的,可以拆开:
f(i,j)=f(i1,j1)∗f(i2,j2)∗...∗f(ik,jk)
下面我们就要考虑如何快速变换了,先将n补到2的次幂,每次把数列分成两半:
DWT(A)i=∑j=0n−1Aj∗f(i,j)
=∑j=0n/2−1Aj∗f(i,j)+∑j=n/2n−1Aj∗f(i,j)
=∑j=0n/2−1Aj∗f(i0,j0)∗f(i1,j1)+∑j=n/2n−1Aj∗f(i0,j0)∗f(i1,j1)
=f(i0,0)∗∑j=0n/2−1Aj∗f(i1,j1)+f(i0,1)∗∑j=n/2n−1Aj∗f(i1,j1)
=f(i0,0)∗DWT(A[0])i1+f(i0,1)∗DWT(A[1])i1
是不是和FFT很像啊,核心就是:
DWT(A)i=f(0,0)∗DWT(A[0])i+f(0,1)∗DWT(A[1])i
DWT(A)i+n/2=f(1,0)∗DWT(A[0])i+f(1,1)∗DWT(A[1])i
(i=0...n/2−1)
递归求解,可以做到
O(nlogn)
。
关于逆转换,其实很简单,直接把之前的过程反着执行一遍即可,相当于解一个二元一次方程。
下面是模板:
#include<cstdio>
#include<algorithm>
using namespace std;
void FWT(int a[],int n,int _k){
for(int m=2;m<=n;m<<=1)
for(int i=0;i<=n-1;i+=m)
for(int j=0;j<=m/2-1;j++){
int t0=a[i+j], t1=a[i+j+m/2];
if(_k==1){
//xor:a[i+j]=t0+t1,a[i+j+m/2]=(t0-t1+mod)%mod;
//and:a[i+j]=t0+t1;
//or:a[i+j+m/2]=t0+t1;
} else{
//xor:a[i+j]=(t0+t1)/2,a[i+j+m/2]=(t0-t1)/2;
//and:a[i+j]=t0-t1;
//or:a[i+j+m/2]=t1-t0;
}
}
}
int main(){
freopen("fwt.in","r",stdin);
freopen("fwt.out","w",stdout);
return 0;
}