我们知道普通的卷积是这样的:
考虑一个变换:
直接给出构造:
⊕为与: 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的次幂,每次把数列分成两半:
是不是和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
#include
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;
}