快速沃尔什变换FWT

快速沃尔什变换\(FWT\)

是一种可以快速完成集合卷积的算法。

什么是集合卷积啊?

集合卷积就是在集合运算下的卷积。比如一般而言我们算的卷积都是\(C_i=\sum_{j+k=i}A_j*B_k\),而集合卷积计算的就是\(C_i=\sum_{j\otimes k=i}A_j*B_k\),其中\(\otimes\)是一种集合运算,可以是与、或、异或。

类似于快速傅里叶变换\(FFT\)\(FWT\)也需要寻求一种变换方式\(FWT(A)\),使\(FWT(C)=FWT(A)*FWT(B)\),其中\(*\)运算就是数组对应下标相乘,时间复杂度是\(O(n)\)的。

或(or)运算的FWT

构造\(FWT(A)=A'\),其中\(A'[i]=\sum_{j\subseteq i}A[j]\)
这样就能满足\(C'=A'*B'\)了。
如何构造?
考虑把\(A\)分成前后两段\(A_0,A_1\),假设\(A\)的长度为\(2^k\)
那么\(A_0\)对应的二进制中第\(k-1\)位一定是\(0\)\(A_1\)对应的二进制中第\(k-1\)位一定是\(1\)
所以\(FWT(A)=merge(FWT(A_0),FWT(A_1)+FWT(A_0))\),其中\(merge\)的意思是把前后两段拼接起来,因为前后两段的长度都是\(2^{k-1}\)

至于\(IFWT?\)
倒推一下就好了。\(IFWT(A')=merge(IFWT(A'_0),IFWT(A'_1)-IFWT(A'_0))\)
代码:

void fwt_or(ll *P,int len,int opt){
	for (int i=1;i<len;i<<=1)
		for (int p=i<<1,j=0;j<len;j+=p)
			for (int k=0;k<i;++k)
				P[j+k+i]+=P[j+k]*opt;
}

与(and)运算的FWT

与或同理。
构造\(FWT(A)=A'\)\(A'[i]=\sum_{i\subseteq j}A[j]\)
\(FWT(A)=merge(FWT(A_0)+FWT(A_1),FWT(A_1))\)
\(IFWT(A')=merge(IFWT(A'_0)-IFWT(A'_1),IFWT(A'_1))\)

代码:

void fwt_and(ll *P,int len,int opt){
	for (int i=1;i<len;i<<=1)
		for (int p=i<<1,j=0;j<len;j+=p)
			for (int k=0;k<i;++k)
				P[j+k]+=P[j+k+i]*opt;
}

异或(xor)运算的FWT

直接上结论吧。
\(FWT(A)=merge(FWT(A_0)+FWT(A_1),FWT(A_0)-FWT(A_1))\)
\(IFWT(A)=merge(\frac{IFWT(A'_0)+IFWT(A'_1)}{2},\frac{IFWT(A'_0)-IFWT(A'_1)}{2})\)
证明出门右转
代码:

void fwt(int *P,int len,int opt){
	for (int i=1;i<len;i<<=1)
		for (int p=i<<1,j=0;j<len;j+=p)
			for (int k=0;k<i;++k)
			{
				int x=P[j+k],y=P[j+k+i];
				P[j+k]=1ll*opt*(x+y)%mod;
				P[j+k+i]=1ll*opt*(x-y+mod)%mod;
			}
}

如果是\(IFWT\)的话就让\(opt=\frac{mod+1}2\)就好了。

posted @ 2018-05-21 16:43  租酥雨  阅读(622)  评论(4编辑  收藏  举报