快速沃尔什变换(FWT)详详详解
文章目录
- 介绍
- 思路
- 或卷积
- 正变换
- 证明
- 求解证明
- 感性理解
- 理性证明
- 逆变换
- 感性理解
- 理性证明
- 与卷积
- 正变换
- 证明
- 求解证明
- 逆变换
- 异或卷积
- 正变换
- 证明
- 求解证明
- 逆变换
- 模板题
介绍
我们以前常见的多项式乘法长这个亚子:
C[k]=∑i+j=kA[i]×B[j]C[k]=\sum_{i+j=k}A[i]\times B[j] C[k]=i+j=k∑A[i]×B[j]
而 FWTFWTFWT 用来处理的,与 FFTFFTFFT 稍稍不同,是这样的卷积:
C[k]=∑i⊕j=kA[i]×B[j]C[k]=\sum_{i\oplus j=k}A[i]\times B[j] C[k]=i⊕j=k∑A[i]×B[j]
其中,⊕\oplus⊕ 可以是 &,∣,Λ\&,|,\Lambda&,∣,Λ,这三种符号分别对应 and,or,xorand,or,xorand,or,xor。
为了方便,首先做一些约定(下面 A,BA,BA,B 代表多项式):
A+B=(A[0]+B[0],A[1]+B[1],⋯)A+B=(A[0]+B[0],A[1]+B[1],\cdots)A+B=(A[0]+B[0],A[1]+B[1],⋯),即按位相加
A−B=(A[0]−B[0],A[1]−B[1],⋯)A-B=(A[0]-B[0],A[1]-B[1],\cdots)A−B=(A[0]−B[0],A[1]−B[1],⋯),即按位相减
A∗B=(A[0]×B[0],A[1]×B[1],⋯)A*B=(A[0]\times B[0],A[1]\times B[1],\cdots)A∗B=(A[0]×B[0],A[1]×B[1],⋯),即按位相乘(这个要注意,别和卷积搞混了)
A⊕B=(∑i⊕j=0A[i]×B[j],∑i⊕j=1A[i]×B[j],⋯)A\oplus B=(\sum\limits_{i\oplus j=0}A[i]\times B[j]~,\sum\limits_{i\oplus j=1}A[i]\times B[j]~,\cdots)A⊕B=(i⊕j=0∑A[i]×B[j] ,i⊕j=1∑A[i]×B[j] ,⋯),本质就是上面的那个卷积
思路
大致思路与 FFTFFTFFT 一致,将要卷在一起的两个多项式先正变换,然后按位相乘,最后逆变换回来即可。
或卷积
卷积的公式是这样的:C[k]=∑i∣j=kA[i]×B[j]C[k]=\sum\limits_{i|j=k}A[i]\times B[j]C[k]=i∣j=k∑A[i]×B[j]。可以表示为 C=A∣BC=A|BC=A∣B。
其实或卷积和与卷积的本质应该是 FMTFMTFMT,但是大部分人将它们归在 FWTFWTFWT 门下了,所以也放在这里讲。
正变换
定义: FWT(A)[i]=∑j∣iA[j]FWT(A)[i]=\sum_{j|i} A[j]FWT(A)[i]=∑j∣iA[j]。这个是正变换后得到的数组的意义,简单来说,就是下标的子集对应的位置之和,其中 j∣ij|ij∣i 表示 jjj 是 iii 的子集。
那么有一个很显然的性质:
FWT(A)+FWT(B)=FWT(A+B)FWT(A)+FWT(B)=FWT(A+B)FWT(A)+FWT(B)=FWT(A+B)
证明: 我们拿每一位单独看:
FWT(A)[i]=∑j∣iA[j]FWT(A)[i]=\sum_{j|i}A[j]FWT(A)[i]=∑j∣iA[j]
FWT(B)[i]=∑j∣iB[j]FWT(B)[i]=\sum_{j|i}B[j]FWT(B)[i]=∑j∣iB[j]
FWT(A+B)[i]=∑j∣i(A+B)[j]=∑j∣iA[j]+B[j]FWT(A+B)[i]=\sum_{j|i}(A+B)[j]=\sum_{j|i}A[j]+B[j]FWT(A+B)[i]=∑j∣i(A+B)[j]=∑j∣iA[j]+B[j]
嗯……很显然可以得到 FWT(A)[i]+FWT(B)[i]=FWT(A+B)[i]FWT(A)[i]+FWT(B)[i]=FWT(A+B)[i]FWT(A)[i]+FWT(B)[i]=FWT(A+B)[i]。
再定义一个东西:设 AAA 这个多项式有 2n2^n2n 项,那么 A0A_0A0 表示前 2n−12^{n-1}2n−1 项,A1A_1A1 表示后 2n−12^{n-1}2n−1 项。
那么其实很容易得到递归的公式:
FWT(A)={(FWT(A0),FWT(A0)+FWT(A1))(n>0)A(n=0)FWT(A)= \begin{cases} (FWT(A_0),FWT(A_0)+FWT(A_1)) & (n>0)\\ A & (n=0) \end{cases} FWT(A)={(FWT(A0),FWT(A0)+FWT(A1))A(n>0)(n=0)
你问我 (A,B)(A,B)(A,B) 这样的表示是什么意思?别忘了 A,BA,BA,B 是多项式,你可以认为这是将两个多项式拼接在了一起。就像 A=(A0,A1)A=(A_0,A_1)A=(A0,A1)。
证明
n=0n=0n=0 时很显然,就不讲了。
仔细观察,A0A_0A0 与 A1A_1A1 的区别在于,A0A_0A0 部分下标最高位是 000,而 A1A_1A1 的最高位是 111,也就是说,A0A_0A0 在对应位置上是 A1A_1A1 的子集,因为只有最高位不一样,其他位都一样,所以我们将 A0A_0A0 加给 A1A_1A1。
又因为 A1A_1A1 中不可能有任何一位是 A0A_0A0 的任意一个位置的子集,所以左半部分就只是 FWT(A0)FWT(A_0)FWT(A0)。
这样递归下去,依次处理完第 n−1n-1n−1 位,第 n−2n-2n−2 位,……,第 111 位后,就求出了 FWT(A)FWT(A)FWT(A)。
求解证明
求出 FWT(A)FWT(A)FWT(A) 和 FWT(B)FWT(B)FWT(B) 之后,对应位置相乘就得到了 FWT(C)FWT(C)FWT(C),然后逆变换就可以得到多项式 CCC。
这里就需要证明,为什么 FWT(C)=FWT(A∣B)=FWT(A)∗FWT(B)FWT(C)=FWT(A|B)=FWT(A)*FWT(B)FWT(C)=FWT(A∣B)=FWT(A)∗FWT(B)。
感性理解
感性理解其实很简单,FWT(A)[i]FWT(A)[i]FWT(A)[i] 记录的是 iii 的子集之和,FWT(B)[i]FWT(B)[i]FWT(B)[i] 也是,那么他们相乘,其实就是双方子集中的每一个元素两两相乘,取出来的两个元素的下标或起来一定还是 iii 的子集中的一个,由于 C[i]C[i]C[i] 记录的是下标或起来为 iii 的乘积之和,那么全部乘完之后,我们不仅得到了 C[i]C[i]C[i],还得到了 iii 的所有子集的 CCC,也就是 FWT(C)[i]FWT(C)[i]FWT(C)[i]。
理性证明
这玩意丑的很……相信没人想看的qwq,不过为了严谨,还是有必要写的啦。
下面为了区分属于…的子集和或运算两个意思,就不都用 ∣|∣ 了,属于…的子集用 ∈\in∈ 代替(只是在这个证明中)。
FWT(C)[i]=∑j∈iC[j]=∑j∈i∑x∣y=jA[x]×B[y]=∑(x∣y)∈iA[x]×B[y]=∑x∈iA[x]×∑y∈iB[y]=FWT(A)[i]×FWT(B)[i]\begin{aligned} FWT(C)[i]&=\sum_{j\in i}C[j]\\ &=\sum_{j\in i}\sum_{x|y=j}A[x]\times B[y]\\ &=\sum_{(x|y)\in i}A[x]\times B[y]\\ &=\sum_{x\in i}A[x]\times\sum_{y\in i}B[y]\\ &=FWT(A)[i]\times FWT(B)[i] \end{aligned} FWT(C)[i]=j∈i∑C[j]=j∈i∑x∣y=j∑A[x]×B[y]=(x∣y)∈i∑A[x]×B[y]=x∈i∑A[x]×y∈i∑B[y]=FWT(A)[i]×FWT(B)[i]
逆变换
逆变换我们称为 IFWTIFWTIFWT,即满足 A=IFWT(FWT(A))A=IFWT(FWT(A))A=IFWT(FWT(A))。
这个其实只需要将上面的加号变成减号即可。
感性理解
可以类比前缀和来理解:
//知道每个位置的值求前缀和
for(int i=1;i<=n;i++)a[i]+=a[i-1];
//知道前缀和求每个位置的值
for(int i=n;i>=1;i--)a[i]-=a[i-1];
理性证明
其实证明也很简单,以及与卷积和异或卷积的理性证明其实都差不多。
证明: 我们现在已知 FWT(A)0,FWT(A)1FWT(A)_0,FWT(A)_1FWT(A)0,FWT(A)1,要求 A0,A1A_0,A_1A0,A1。
∵FWT(A)0=FWT(A0)\because FWT(A)_0=FWT(A_0)∵FWT(A)0=FWT(A0)
∴A0=IFWT(FWT(A0))=IFWT(FWT(A)0)\therefore A_0=IFWT(FWT(A_0))=IFWT(FWT(A)_0)∴A0=IFWT(FWT(A0))=IFWT(FWT(A)0)
∵FWT(A)1=FWT(A0)+FWT(A1)\because FWT(A)_1=FWT(A_0)+FWT(A_1)∵FWT(A)1=FWT(A0)+FWT(A1),即 FWT(A1)=FWT(A)1−FWT(A)0FWT(A_1)=FWT(A)_1-FWT(A)_0FWT(A1)=FWT(A)1−FWT(A)0
∴A1=IFWT(FWT(A1))=IFWT(FWT(A)1−FWT(A)0)\therefore A_1=IFWT(FWT(A_1))=IFWT(FWT(A)_1-FWT(A)_0)∴A1=IFWT(FWT(A1))=IFWT(FWT(A)1−FWT(A)0)
代码实现(其实和 FFTFFTFFT 很像):
void FWT_or(ll *f,int type)//type为1是正变换,-1是逆变换
{for(int mid=1;mid<n;mid<<=1)//每次将两块大小为mid的块合并成一个大块for(int block=mid<<1,j=0;j<n;j+=block)//枚举每个大块for(int i=j;i<j+mid;i++)//依次合并f[i+mid]=(f[i+mid]+f[i]*type+mod)%mod;
}
与卷积
卷积的公式是这样的:C[k]=∑i&j=kA[i]×B[j]C[k]=\sum\limits_{i\&j=k}A[i]\times B[j]C[k]=i&j=k∑A[i]×B[j]。
正变换
定义: FWT(A)[i]=∑i∣jA[j]FWT(A)[i]=\sum_{i|j}A[j]FWT(A)[i]=∑i∣jA[j],意思就是,FWT(A)[i]FWT(A)[i]FWT(A)[i] 记录的是所有 A[j]A[j]A[j] 的和,其中 i,ji,ji,j 满足 iii 是 jjj 的子集。
这个显然也满足 FWT(A)+FWT(B)=FWT(A+B)FWT(A)+FWT(B)=FWT(A+B)FWT(A)+FWT(B)=FWT(A+B)。
其实与卷积和或卷积十分相似,一个是前面的对后面的产生贡献,一个是后面的对前面产生贡献。
类比或卷积,可以得到与卷积的递归公式:
FWT(A)={(FWT(A0)+FWT(A1),FWT(A1))(n>0)A(n=0)FWT(A)= \begin{cases} (FWT(A_0)+FWT(A_1),FWT(A_1)) & (n>0)\\ A & (n=0) \end{cases} FWT(A)={(FWT(A0)+FWT(A1),FWT(A1))A(n>0)(n=0)
证明
这就不用证了吧……和或卷积基本上一毛一样,就是 A0A_0A0 和 A1A_1A1 对应位置就差一个 111,所以 A0A_0A0 是 A1A_1A1 子集,所以把 A1A_1A1 的贡献累加到 A0A_0A0 上。
求解证明
如果理解了或运算的求解证明,那么与运算的其实也搞定了。
这里就直接贴一个数学证明了:
FWT(C)[i]=∑i∈jC[j]=∑i∈j∑x∣y=jA[x]×B[y]=∑i∈(x∣y)A[x]×B[y]=∑i∈xA[x]×∑i∈yB[y]=FWT(A)[i]×FWT(B)[i]\begin{aligned} FWT(C)[i]&=\sum_{i\in j}C[j]\\ &=\sum_{i\in j}\sum_{x|y=j}A[x]\times B[y]\\ &=\sum_{i\in(x|y)}A[x]\times B[y]\\ &=\sum_{i\in x}A[x]\times\sum_{i\in y}B[y]\\ &=FWT(A)[i]\times FWT(B)[i] \end{aligned} FWT(C)[i]=i∈j∑C[j]=i∈j∑x∣y=j∑A[x]×B[y]=i∈(x∣y)∑A[x]×B[y]=i∈x∑A[x]×i∈y∑B[y]=FWT(A)[i]×FWT(B)[i]
几乎就是一样的qwq
逆变换
也是很简单的将加号变减号。
证明: 我们现在已知 FWT(A)0,FWT(A)1FWT(A)_0,FWT(A)_1FWT(A)0,FWT(A)1,要求 A0,A1A_0,A_1A0,A1。
∵FWT(A)1=FWT(A1)\because FWT(A)_1=FWT(A_1)∵FWT(A)1=FWT(A1)
∴A1=IFWT(FWT(A1))=IFWT(FWT(A)1)\therefore A_1=IFWT(FWT(A_1))=IFWT(FWT(A)_1)∴A1=IFWT(FWT(A1))=IFWT(FWT(A)1)
∵FWT(A)0=FWT(A0)+FWT(A1)\because FWT(A)_0=FWT(A_0)+FWT(A_1)∵FWT(A)0=FWT(A0)+FWT(A1),即 FWT(A0)=FWT(A)1−FWT(A)0FWT(A_0)=FWT(A)_1-FWT(A)_0FWT(A0)=FWT(A)1−FWT(A)0
∴A0=IFWT(FWT(A0))=IFWT(FWT(A)0−FWT(A)1)\therefore A_0=IFWT(FWT(A_0))=IFWT(FWT(A)_0-FWT(A)_1)∴A0=IFWT(FWT(A0))=IFWT(FWT(A)0−FWT(A)1)
代码实现:
void FWT_and(ll *f,int type)
{for(int mid=1;mid<n;mid<<=1)for(int block=mid<<1,j=0;j<n;j+=block)for(int i=j;i<j+mid;i++)f[i]=(f[i]+f[i+mid]*type+mod)%mod;
}
异或卷积
这个东西才是真正的 FWTFWTFWT,求法都奇奇怪怪的。
卷积的公式是这样的:C[k]=∑iΛj=kA[i]×B[j]C[k]=\sum\limits_{i\Lambda j=k}A[i]\times B[j]C[k]=iΛj=k∑A[i]×B[j]。
正变换
首先摒弃上面带来的一些奇奇怪怪的想法,因为异或卷积的正变换没有用到异或,完全没有。
设 c(i)c(i)c(i) 表示 iii 的二进制有多少个 111,如 c(3)=2c(3)=2c(3)=2。
奇怪的定义: FWT(A)[i]=∑j=02n−1(−1)c(i&j)A[j]FWT(A)[i]=\sum\limits_{j=0}^{2^n-1}(-1)^{c(i\&j)}A[j]FWT(A)[i]=j=0∑2n−1(−1)c(i&j)A[j],也可以写成 FWT(A)[i]=(∑c(i&j)mod2=0A[j])−(∑c(i&k)mod2=1A[k])FWT(A)[i]=(\sum\limits_{c(i\&j)\bmod 2=0}A[j])-(\sum\limits_{c(i\&k)\bmod 2=1}A[k])FWT(A)[i]=(c(i&j)mod2=0∑A[j])−(c(i&k)mod2=1∑A[k])。
没错,是 &\&& 不是 Λ\LambdaΛ。不用管为什么,看下去就知道了,重点是记住定义。
递归公式:
FWT(A)={(FWT(A0)+FWT(A1),FWT(A0)−FWT(A1))(n>0)A(n=0)FWT(A)= \begin{cases} (FWT(A_0)+FWT(A_1),FWT(A_0)-FWT(A_1)) & (n>0)\\ A & (n=0) \end{cases} FWT(A)={(FWT(A0)+FWT(A1),FWT(A0)−FWT(A1))A(n>0)(n=0)
怎么样,是不是有种见了鬼的感觉qwq
但是实际上不难理解。
证明
依然忽略 n=0n=0n=0 时的情况~
此时要理解,求出来的 FWT(A1)FWT(A_1)FWT(A1) 并没有考虑到他是 AAA 的右半部分,即最高位的 111 是没有考虑的,也就是说,求 FWT(A1)FWT(A_1)FWT(A1) 时下标是 000 ~ 2n−1−12^{n-1}-12n−1−1,而不是 2n−12^{n-1}2n−1 ~ 2n−12^n-12n−1。
剩下的我们从左到右看:
首先 FWT(A)0=FWT(A0)+FWT(A1)FWT(A)_0=FWT(A_0)+FWT(A_1)FWT(A)0=FWT(A0)+FWT(A1),因为 FWT(A)0FWT(A)_0FWT(A)0 是左半部分,于是最高位是 000,所以不管是和左半部分做与运算还是与右半部分做与运算,最高位都是 000,所以直接把左右两边的贡献加起来即可。
而 FWT(A)1FWT(A)_1FWT(A)1 比较特殊,他是右半部分,所以他与右半部分做与运算时,最高位多出来一个 111,而这是 FWT(A1)FWT(A_1)FWT(A1) 没有考虑到的,如果多了个 111 的话,回看上面的定义,c(i&j)c(i\&j)c(i&j) 就会乘多一个 −1-1−1,所以 FWT(A1)FWT(A_1)FWT(A1) 的贡献是负的。而 FWT(A0)FWT(A_0)FWT(A0) 无所谓,由于它本身就在左半部分,不会多出最高位的 111,所以贡献是正的。
求解证明
这里就用到异或了,以及用到了一个性质(描述有点长qwq,不想看的可以直接看下面的柿子):iii 与 kkk 的 111 的数量的奇偶性异或 jjj 与 kkk 的 111 的数量的奇偶性等于 iii 异或 jjj 再与 kkk 的 111 的数量的奇偶性相同。
用数学柿子来表示的话,设 P(i)P(i)P(i) 表示 iii 的奇偶性,当 iii 是奇数时 P(i)=1P(i)=1P(i)=1,当 iii 是偶数时 P(i)=0P(i)=0P(i)=0,那么这个性质就是:
P(c(i&k))ΛP(c(j&k))=P(c((iΛj)&k))P(c(i\&k))\Lambda P(c(j\&k))=P(c((i\Lambda j)\&k)) P(c(i&k))ΛP(c(j&k))=P(c((iΛj)&k))
为了严谨,证明还是要有的(其实随便手玩一下就懂了,很简单):
考虑 i,j,ki,j,ki,j,k 的某一位,记为 x,y,zx,y,zx,y,z(注意,是同一位),那么有两种情况(已经尽力压缩了qwq,怕多了各位不看呀,下面这些看起来繁琐的东西其实逻辑很简单的,静心细看就好):
- x,yx,yx,y 都等于或不等于 zzz(即 x=yx=yx=y)。那么 i&ki\&ki&k 和 j&kj\&kj&k 的这一位相同,所以 c(i&k)c(i\&k)c(i&k) 和 c(j&k)c(j\&k)c(j&k) 同时 +1+1+1 或 +0+0+0,P(c(i&k))ΛP(c(j&k))P(c(i\&k))\Lambda P(c(j\&k))P(c(i&k))ΛP(c(j&k)) 的值不变。再看等式右边,iΛji\Lambda jiΛj 的这一位为 000,那么 (iΛj)&k(i\Lambda j)\&k(iΛj)&k 的这一位就是 000,对 c((iΛj)&k)c((i\Lambda j)\&k)c((iΛj)&k) 没有贡献,所以 P(c((iΛj)&k))P(c((i\Lambda j)\&k))P(c((iΛj)&k)) 也不会变,等式成立。
- x,yx,yx,y 一个等于 zzz,一个不等于 zzz(即 x≠yx\neq yx=y)。假如 z=0z=0z=0,那么参照情况 111,很容易知道此时两边又是不会发生变化的。如果 z=1z=1z=1,此时 i&ki\&ki&k 和 j&kj\&kj&k 的这一位不同,所以 c(i&k)c(i\&k)c(i&k) 和 c(j&k)c(j\&k)c(j&k) 一个 +1+1+1,一个 +0+0+0,那么等式左边就会发生变化。而右边因为 iΛji\Lambda jiΛj 和 kkk 的这一位同时为 111,所以 c((iΛj)&k)c((i\Lambda j)\&k)c((iΛj)&k) 会 +1+1+1,那么等式右边也会发生变化。等式两边同时变化,等式依然成立。
接下来就是求解的证明了,还是像上面那样大力展开(自己手(luan)推的,要是错了告知一下谢谢啦qwq):
FWT(C)[i]=∑j=02n−1(−1)c(i&j)C[j]=∑j=02n−1(−1)c(i&j)∑xΛy=jA[x]×B[y]=∑x=02n−1∑y=02n−1A[x]×B[y]×(−1)c(i&(xΛy))\begin{aligned} FWT(C)[i]&=\sum\limits_{j=0}^{2^n-1}(-1)^{c(i\&j)}C[j]\\ &=\sum\limits_{j=0}^{2^n-1}(-1)^{c(i\&j)}\sum_{x\Lambda y=j} A[x]\times B[y]\\ &=\sum_{x=0}^{2^n-1}\sum_{y=0}^{2^n-1}A[x]\times B[y]\times(-1)^{c(i\&(x\Lambda y))} \end{aligned} FWT(C)[i]=j=0∑2n−1(−1)c(i&j)C[j]=j=0∑2n−1(−1)c(i&j)xΛy=j∑A[x]×B[y]=x=0∑2n−1y=0∑2n−1A[x]×B[y]×(−1)c(i&(xΛy))
诶!仔细观察发现,我们只在意 c(i&(xΛy))c(i\&(x\Lambda y))c(i&(xΛy)) 的奇偶性,因为他是 −1-1−1 的指数,所以我们不妨在外面套一个 PPP,显然是不影响答案的。
=∑x=02n−1∑y=02n−1A[x]×B[y]×(−1)P(c(i&(xΛy)))=\sum_{x=0}^{2^n-1}\sum_{y=0}^{2^n-1}A[x]\times B[y]\times(-1)^{P(c(i\&(x\Lambda y)))} =x=0∑2n−1y=0∑2n−1A[x]×B[y]×(−1)P(c(i&(xΛy)))
这不就是上面性质里的样子吗!于是我们可以愉快的拆开啦:
=∑x=02n−1∑y=02n−1A[x]×B[y]×(−1)P(c(i&x))ΛP(c(i&y))=\sum_{x=0}^{2^n-1}\sum_{y=0}^{2^n-1}A[x]\times B[y]\times(-1)^{P(c(i\&x))~\Lambda ~P(c(i\&y))} =x=0∑2n−1y=0∑2n−1A[x]×B[y]×(−1)P(c(i&x)) Λ P(c(i&y))
仔细观察下面的柿子,是不是有什么规律?
(−1)0Λ0=1=(−1)0+0(-1)^{0\Lambda 0}=~~~1=(-1)^{0+0}(−1)0Λ0= 1=(−1)0+0
(−1)0Λ1=−1=(−1)0+1(-1)^{0\Lambda 1}=-1=(-1)^{0+1}(−1)0Λ1=−1=(−1)0+1
(−1)1Λ0=−1=(−1)1+0(-1)^{1\Lambda 0}=-1=(-1)^{1+0}(−1)1Λ0=−1=(−1)1+0
(−1)1Λ1=1=(−1)1+1(-1)^{1\Lambda 1}=~~~1=(-1)^{1+1}(−1)1Λ1= 1=(−1)1+1
没错!将指数里的 Λ\LambdaΛ 换成 +++ 并不影响答案!
接下来就可以顺理成章的证明下去了。
=∑x=02n−1∑y=02n−1A[x]×B[y]×(−1)P(c(i&x))+P(c(i&y))=∑x=02n−1∑y=02n−1A[x]×B[y]×(−1)P(c(i&x))×(−1)P(c(i&y))=∑x=02n−1A[x]×(−1)P(c(i&x))×∑y=02n−1B[y]×(−1)P(c(i&y))=FWT(A)[i]×FWT(B)[i]\begin{aligned} &=\sum_{x=0}^{2^n-1}\sum_{y=0}^{2^n-1}A[x]\times B[y]\times(-1)^{P(c(i\&x))+P(c(i\&y))}\\ &=\sum_{x=0}^{2^n-1}\sum_{y=0}^{2^n-1}A[x]\times B[y]\times(-1)^{P(c(i\&x))}\times(-1)^{P(c(i\&y))}\\ &=\sum_{x=0}^{2^n-1}A[x]\times (-1)^{P(c(i\&x))} \times \sum_{y=0}^{2^n-1}B[y]\times (-1)^{P(c(i\&y))}\\ &=FWT(A)[i]\times FWT(B)[i] \end{aligned} =x=0∑2n−1y=0∑2n−1A[x]×B[y]×(−1)P(c(i&x))+P(c(i&y))=x=0∑2n−1y=0∑2n−1A[x]×B[y]×(−1)P(c(i&x))×(−1)P(c(i&y))=x=0∑2n−1A[x]×(−1)P(c(i&x))×y=0∑2n−1B[y]×(−1)P(c(i&y))=FWT(A)[i]×FWT(B)[i]
逆变换
这个就稍微跟上面不一样的,不过一样的是都很容易理解~
设 A′=FWT(A)A'=FWT(A)A′=FWT(A),那么逆变换递推公式就是:
{(IFWT(A0′)+IFWT(A1′)2,IFWT(A0′)−IFWT(A1′)2)(n>0)A′(n=0)\begin{cases} (\frac {IFWT(A'_0)+IFWT(A'_1)} 2,\frac {IFWT(A'_0)-IFWT(A'_1)} 2) & (n>0)\\ A' & (n=0) \end{cases} {(2IFWT(A0′)+IFWT(A1′),2IFWT(A0′)−IFWT(A1′))A′(n>0)(n=0)
证明: 现在已知 FWT(A)0,FWT(A)1FWT(A)_0,FWT(A)_1FWT(A)0,FWT(A)1,求 A0,A1A_0,A_1A0,A1。
∵FWT(A)0=FWT(A0)+FWT(A1),FWT(A)1=FWT(A0)−FWT(A1)\because FWT(A)_0=FWT(A_0)+FWT(A_1),FWT(A)_1=FWT(A_0)-FWT(A_1)∵FWT(A)0=FWT(A0)+FWT(A1),FWT(A)1=FWT(A0)−FWT(A1)
∴FWT(A0)=FWT(A)0+FWT(A)12,FWT(A1)=FWT(A)0−FWT(A)12\therefore FWT(A_0)=\frac {FWT(A)_0+FWT(A)_1} 2,FWT(A_1)=\frac {FWT(A)_0-FWT(A)_1} 2∴FWT(A0)=2FWT(A)0+FWT(A)1,FWT(A1)=2FWT(A)0−FWT(A)1
∴A0=IFWT(FWT(A0))=IFWT(FWT(A)0+FWT(A)12)\therefore A_0=IFWT(FWT(A_0))=IFWT(\frac {FWT(A)_0+FWT(A)_1} 2)∴A0=IFWT(FWT(A0))=IFWT(2FWT(A)0+FWT(A)1)
A1=IFWT(FWT(A1))=IFWT(FWT(A)0−FWT(A)12)~~~~A_1=IFWT(FWT(A_1))=IFWT(\frac {FWT(A)_0-FWT(A)_1} 2) A1=IFWT(FWT(A1))=IFWT(2FWT(A)0−FWT(A)1)
代码实际上也没比上面的麻烦多少:
void FWT_xor(ll *f,int type)
{for(int mid=1;mid<n;mid<<=1)for(int block=mid<<1,j=0;j<n;j+=block)for(int i=j;i<j+mid;i++)//这三个循环与上面完全一样{ll x=f[i],y=f[i+mid];f[i]=(x+y)%mod*(type==1?1:inv_2)%mod;f[i+mid]=(x-y+mod)%mod*(type==1?1:inv_2)%mod;}
}
模板题
题目传送门
#include <cstdio>
#include <cstring>
#define ll long long
#define mod 998244353
#define maxn 1<<18int n;
ll a[maxn],b[maxn],A[maxn],B[maxn];
void FWT_or(ll *f,int type)
{for(int mid=1;mid<n;mid<<=1)for(int block=mid<<1,j=0;j<n;j+=block)for(int i=j;i<j+mid;i++)f[i+mid]=(f[i+mid]+f[i]*type+mod)%mod;
}
void FWT_and(ll *f,int type)
{for(int mid=1;mid<n;mid<<=1)for(int block=mid<<1,j=0;j<n;j+=block)for(int i=j;i<j+mid;i++)f[i]=(f[i]+f[i+mid]*type+mod)%mod;
}
int inv_2=499122177;
void FWT_xor(ll *f,int type)
{for(int mid=1;mid<n;mid<<=1)for(int block=mid<<1,j=0;j<n;j+=block)for(int i=j;i<j+mid;i++){ll x=f[i],y=f[i+mid];f[i]=(x+y)%mod*(type==1?1:inv_2)%mod;f[i+mid]=(x-y+mod)%mod*(type==1?1:inv_2)%mod;}
}
void work(void (*FWT)(ll *f,int type))//将函数作为参数传入
{for(int i=0;i<n;i++)a[i]=A[i],b[i]=B[i];FWT(a,1);FWT(b,1);for(int i=0;i<n;i++)a[i]=a[i]*b[i]%mod;FWT(a,-1);for(int i=0;i<n;i++)printf("%lld ",a[i]);printf("\n");
}int main()
{scanf("%d",&n);n=1<<n;for(int i=0;i<n;i++)scanf("%lld",&A[i]),A[i]%=mod;for(int i=0;i<n;i++)scanf("%lld",&B[i]),B[i]%=mod;work(FWT_or);work(FWT_and);work(FWT_xor);
}
快速沃尔什变换(FWT)详详详解相关推荐
- 解题报告(一)快速沃尔什变换FWT(ACM / OI)超高质量题解
整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 繁凡出品的全新系列:解题报告系列 -- 超高质量算法题单,配套我写的超高质量的题解和代码,题目难度不一 ...
- 快速沃尔什变换(FWT)
1前言 在之前学完了FFT稍微码了一些题.也学习了一下NTT相关的知识之后,我觉得有必要学习一下FWT,这篇博客就是阐述我对FWT的理解的 2介绍 2.1解决的问题 对于FFT,它的过程本质上是cn= ...
- 【learning】快速沃尔什变换FWT
问题描述 已知\(A(x)\)和\(B(x)\),\(C[i]=\sum\limits_{j\otimes k=i}A[j]*B[k]\),求\(C\) 其中\(\otimes\)是三种位运算的其中一 ...
- [学习笔记]快速沃尔什变换 (FWT)
FWT的简介 一般 FWT \text{FWT} FWT用来解决一下问题: C k = ∑ i ∣ j = k A i B j C_k=\sum_{i|j=k}A_iB_j Ck=∑i∣j=kAi ...
- 快速沃尔什变换(FWT)及K进制异或卷积快速子集变换(FST)讲解
前言: $FWT$是用来处理位运算(异或.与.或)卷积的一种变换.位运算卷积是什么?形如$f[i]=\sum\limits_{j\oplus k==i}^{ }g[j]*h[k]$的卷积形式(其中$\ ...
- 快速沃尔什变换(FWT) 学习笔记
最近在做多校联赛的题目,发现有一道题需要用到FWT,于是我就去学了一下.膜拜一下大神,本篇博客仅对这篇博客进行一些细节上的补充. FWT要解决的问题是 Ck=∑i⊕j=kai∗bi {C}_{k}=\ ...
- 《小学生都能看懂的快速沃尔什变换从入门到升天教程》(FWT / FMT / FMI)(最最严谨清晰的证明!零基础也能得学会!)
整理的算法模板合集: ACM模板 点我看算法全家桶系列!!! 实际上是一个全新的精炼模板整合计划 目录 0x00 卷积 0x01 多项式 0x02 卷积的定义 0x03 卷积的基本性质 0x04 位运 ...
- [多项式算法](Part 4)FWT 快速沃尔什变换 学习笔记
其他多项式算法传送门: [多项式算法](Part 1)FFT 快速傅里叶变换 学习笔记 [多项式算法](Part 2)NTT 快速数论变换 学习笔记 [多项式算法](Part 3)MTT 任意模数FF ...
- 快速沃尔什变换(FWT)讲解 解决集合卷积的方法
由于某次CSDN更新,文章样式已经丢失,大量公式无法正常显示,本博客也不再维护. 请到快速沃尔什变换FWT查看,有更加完整的公式讲解. 能看到这篇博客的人,一定知道FWT是干什么的.(什么?你不知道? ...
- [模板] 快速沃尔什变换
快速沃尔什变换 快速沃尔什变换是求这样的式子: 对序列 \(A\), \(B\), 求序列 \(C\), 使得 \[ C_{i}=\sum_{j \oplus k} A_{j} B_{k} \] 其中 ...
最新文章
- Hibernate关联关系映射
- Linux MySQL 忘记root 密码
- NLP复习资料(8)-知识图谱、信息抽取
- PAT 1014 Waiting in Line
- linux怎样卸载conda,【原创文章】生信软件环境conda的安装与卸载
- 在php里bd2什么意思,PSR-2 PHP三元语法中是否需要括号?
- 概述Swing窗体的种类
- Spring Cloud 子项目
- 二维随机变量期望公式_多维随机变量的特征数
- 初次项目:电子通讯录
- 停车场web项目(内含有数据库)
- 【天下有春】剑气纵横三万里,一剑光寒十九洲
- 2023全球边缘计算大会·北京站成功召开!
- C语言复数运算(结构体)
- linux md文件 编辑,用Vim写md文档的简单姿势
- 牛顿迭代法 matlab程序
- Spark中组件Mllib的学习16之分布式行矩阵的四种形式
- 深度 | Nature论文:无监督表征学习,用电子健康病历增强临床决策
- 当商业空间撞见元宇宙艺术
- MySQL数据库13——插入数据(INSERT)
热门文章
- this和spuer的区别
- Spark-Core源码学习记录 3 SparkContext、SchedulerBackend、TaskScheduler初始化及应用的注册流程
- ORA-01221: data file 5 is not the same file to a background process
- AIBlockChain:“知名博主独家讲授”人工智能创新应用竞赛【精选实战作品】之《基于计算机视觉、自然语言处理、区块链和爬虫技术的智能会议系统》软件系统案例的界面简介、功能介绍分享之四、数据挖掘
- 求救:varlist not allowed#stata
- 第七天;信息打点-资产泄漏CMS识别Git监控SVNDS_Store备份
- 更有效的桌面环境切换方式
- 【问题记录】VeeValidate配置中文的两种不同做法
- waitfor 的用法
- 计算机文件用英语怎么说,电脑里的文件是什么意思