快速沃尔什变换-FWT

0. 引入

快速沃尔什变换(FWT) 是用来解决位运算卷积的算法,形如:

\[C_k=\sum_{i\oplus j=k}A_i\times B_j \]

其中,\(\oplus\) 代表位运算中的某一种


1. 转化

回顾 FFT/NTT,加法卷积是将表达式转化成点值,相乘后再转化回来

那我们考虑构造一个幂级数,使其与点值有类似的功能,即:

\[A*B=C\Leftrightarrow FWT(A)\times FWT(B)=FWT(C) \]

我们考虑设 \(c(i,j)\) 为转换系数,满足

\[FWT(A)_i=\sum_{j=0}^{n-1}c(i,j)\times A_j \]

\(FWT(A)_i\times FWT(B)_i=FWT(C)_i\),我们需要:

\[\sum_{j=0}^{n-1}c(i,j)A_j\times \sum_{k=0}^{n-1}c(i,k)B_k=\sum_{p=0}^{n-1}c(i,p)C_p \]

\[\sum_{j=0}^{n-1}\sum_{k=0}^{n-1}c(i,j)c(i,k)A_jB_k=\sum_{p=0}^{n-1}c(i,p)C_p \]

根据 \(C_k=\sum_{i\oplus j=k}A_i\times B_j\),有

\[\sum_{j=0}^{n-1}\sum_{k=0}^{n-1}c(i,j)c(i,k)A_jB_k=\sum_{p=0}^{n-1}\sum_{x\oplus y=p}c(i,p)A_xB_y \]

\[\sum_{j=0}^{n-1}\sum_{k=0}^{n-1}c(i,j)c(i,k)A_jB_k=\sum_{x=0}^{n-1}\sum_{y=0}^{n-1}c(i,x\oplus y)A_xB_y \]

这说明,我们需要构造出 \(c\) 满足 \(c(i,j)c(i,k)=c(i,j\oplus k)\)

同时,\(c\) 应该还要满足一个条件:我们假设对于一个二进制数 \(a\),它从高到低位表示为 \(a_0,a_1,a_2,...\)

那么应该有 \(c(i,j)=c(i_0,j_0)c(i_1,j_1)c(i_2,j_2)...\)

这样,我们就可以推出 \(c(i_x,j_x)c(i_x,k_x)=c(i_x,j_x\oplus k_x)\Leftrightarrow c(i,j)c(i,k)=c(i,j\oplus k)\)

因此,假设我们构造出了 \(c(0,0),c(0,1),c(1,0),c(1,1)\),我们看一下如何求解 \(FWT(A)\)

我们考虑按位折半

\[FWT(A)_i=\sum_{j=0}^{n/2-1}c(i,j)A_j+\sum_{j=n/2}^{n-1}c(i,j)A_j \]

\(i'\) 表示 \(i\) 去掉二进制首位(包括 \(0,1\))的数,就有:

\[FWT(A)_i=c(i_0,0)\sum_{j=0}^{n/2-1}c(i',j')A_j+c(i_0,1)\sum_{j=n/2}^{n-1}c(i',j')A_j \]

说明我们可以将问题分治下去,同时我们要构造一个矩阵:

\[\begin{bmatrix} c(0,0) & c(0,1)\\ c(1,0) & c(1,1) \end{bmatrix}\]

而且这个矩阵有逆,用于 IFWT

我们假设分治到求长度为 \(n\)FWT\(A_0\) 表示二进制首位为 \(0\) 的幂级数(即已经求出的长为 \(n/2\)FWT),\(A_1\) 同理

有:

\[FWT[A]_i=c(0,0)FWT[A_0]_i+c(0,1)FWT[A_1]_i\ (i\in [0,n/2]) \]

\[FWT[A+n/2]_i=c(1,0)FWT[A_0]_i+c(1,1)FWT[A_1]_i\ (i\in [0,n/2]) \]


2. 构造

接下来我们就要学习怎么构造位矩阵(记结论!)

① 或(or)卷积

由定义,我们有:

\[c(0,0)c(0,0)=c(0,0) \]

所以 \(c(0,0)=0/1\)

同理 其他都只能取 \(0/1\)

然后又需要满足:

\[c(0,0)c(0,1)=c(0,1) \]

\[c(1,0)c(1,1)=c(1,1) \]

因为如果存在某一行或某一列都为 \(0\) 时,矩阵没有逆,所以我们只有这两种构造:

\[\begin{bmatrix} 1 & 1\\ 1 & 0 \end{bmatrix} \begin{bmatrix} 1 & 0\\ 1 & 1 \end{bmatrix}\]

我们选用第二个矩阵,其逆矩阵为:

\[\begin{bmatrix} 1 & 0\\ -1 & 1 \end{bmatrix}\]

运算为:

\[FWT[A]_i=FWT[A_0]_i \]

\[FWT[A+n/2]_i=FWT[A_0]_i+FWT[A_1]_i \]

逆运算为:

\[IFWT[A]_i=IFWT[A_0]_i \]

\[IFWT[A+n/2]_i=-IFWT[A_0]_i+IFWT[A_1]_i \]

② 和(and)卷积

同上,我们有:

\[c(x,y)c(x,y)=c(x,y) \]

所以 \(c(x,y)=0/1\)

\[c(0,0)c(0,1)=c(0,0) \]

\[c(1,0)c(1,1)=c(1,0) \]

所以有两种矩阵:

\[\begin{bmatrix} 0 & 1\\ 1 & 1 \end{bmatrix} \begin{bmatrix} 1 & 1\\ 0 & 1 \end{bmatrix}\]

依旧取第二个矩阵,其逆为:

\[\begin{bmatrix} 1 & -1\\ 0 & 1 \end{bmatrix}\]

运算为:

\[FWT[A]_i=FWT[A_0]_i+FWT[A_1]_i \]

\[FWT[A+n/2]_i=FWT[A_1]_i \]

逆运算为:

\[IFWT[A]_i=IFWT[A_0]_i-IFWT[A_1]_i \]

\[IFWT[A+n/2]_i=IFWT[A_1]_i \]

③ 异或(Xor)卷积

有以下关系式:

\[c(x,y)c(x,y)=c(0,0) \]

\[c(0,0)c(0,1)=c(0,1) \]

\[c(1,0)c(1,1)=c(1,1) \]

所以 \(c(0,0)=c(1,0)=1\),其他的为 \(1/-1\)

所以有:

\[\begin{bmatrix} 1 & -1\\ 1 & 1 \end{bmatrix} \begin{bmatrix} 1 & 1\\ 1 & -1 \end{bmatrix}\]

继续选用第二个矩阵,其逆为:

\[\begin{bmatrix} 0.5 & 0.5\\ 0.5 & -0.5 \end{bmatrix}\]

运算为:

\[FWT[A]_i=FWT[A_0]_i+FWT[A_1]_i \]

\[FWT[A+n/2]_i=FWT[A_0]_i-FWT[A_1]_i \]

逆运算为:

\[IFWT[A]_i=\frac{1}{2}IFWT[A_0]_i+\frac{1}{2}IFWT[A_1]_i \]

\[IFWT[A+n/2]_i=\frac{1}{2}IFWT[A_0]_i-\frac{1}{2}IFWT[A_1]_i \]

然后我们就可以做了!

代码


FWT 有是线性变换,也就是说它有如下性质:

\[FWT(A+B)=FWT(A)+FWT(B) \]

3. 例题

CF449D Jzzhu and Numbers

这是一个背包,我们有转移方程为:

\[dp_{i,j}=dp_{i-1,j} +\sum_{a[i]\&k=j}dp_{i-1,k} \]

我们就可以构造出 \(n\) 个函数(设 \(Mx = \max(a_i)\)\(m\) 为最小的满足 \(2^m-1\ge Mx\) 的正整数),第 \(i\) 个函数为:\(f_i[2^m-1]=f_i[a[i]]=1\),其余系数为 \(0\)

\(f_i[2^m-1]=1\) 表示 \(a[i]\) 不选的情况

我们将这些函数全部卷起来后,\([x^0]\) 上的系数就是答案

但直接卷的复杂度是 \(O(n^2\log n)\) 的,还不如原来的 \(O(n^2)\) 直接做

我们注意到,每个函数都只有两项为 \(1\),我们可以考虑进行一些优化

\[FWT(A_i)_k=\sum_{j=0}^{2^m-1}c(k, j)A_i[j]=c(k,2^m-1)+c(k,a_i) \]

由于我们是 and卷积,选择的矩阵为 \(\begin{bmatrix} 1 & 1\\ 0 & 1 \end{bmatrix}\)

所以有 \(c(i,j)=[i\&j=i]\)

这说明,\(c(k,2^m-1)\) 一定为 \(1\),而 \(c(k,a_i)\) 只有当 \(k\& a_i=k\) 时才会为 \(1\)

我们再考虑设 \(F_k=\prod_{i=1}^{n}FWT(A_i)_k\)

那么 \([x^0]IFWT[F]\) 的系数就是答案

所以,\(F_k\) 一定为 \(2\) 的整次幂

我们的任务就是统计有多少个 \(2\) 相乘

实际上,\(a_i\)会对它的子集进行贡献

所以,我们可以通过子集求和,也就是做高维后缀和来求,然后再求 IFWT 即可

代码

还有一种方法,我们设 \(G[a_i]\)\(a_i\) 出现的次数,求出 \(FWT(G)\),那么 \(FWT(G)_k\) 就是 \(F_k\)\(2\) 的个数

证明:

  • \[FWT(G)_k=\sum_{j=0}^{2^m-1}c(k,j)G[j]=\sum_{j=0}^{2^m-1}c(k,j)\sum_{i=1}^{n}[a_i=j]=\sum_{i=1}^nc(k,a_i) \]

代码

(这两种方法的复杂度相同,但可以看出还是高维前缀和略胜一筹)

UOJ310 黎明前的巧克力

依然考虑 DP,有转移方程:

\[dp_{i,j}=dp_{i-1,j}+2\times \sum_{a[i]\oplus k=j}dp_{i-1,k} \]

构造 \(n\) 个函数,使 \(F_i[0]=1,\ F_i[a[i]]=2\)

直接卷依旧是 \(O(n^2\log n)\)

对于每个函数,有 \(FWT(F_i)[k]=\sum_{j=0}^{lim}c(k,j)F_i[j]\)

异或卷积 矩阵的性质,我们知道:

\[c(i,j)=(-1)^{pop\_cnt(i\&j)} \]

所以原式有 \(FWT(F_i)[k]=1+(-1)^{pop\_cnt(k\&a[i])}\times 2\)

那么就可能取 \(-1\)\(3\)

由于 FWT(A+B)=FWT(A)+FWT(B),我们可以考虑将 \(n\) 个函数相加得到一个函数 \(G\),变换为 FWT 后,我们就有 \(FWT(G)[k]=\sum_{i=1}^n FWT(F_i)[k]\)

由于我们原答案需要的是 \(\prod_{i=1}^n FWT(Fi)[k]\),我们可以解出 \(-1,3\) 的个数后,通过快速幂求出

代码

CF1119H Triple

套路是类似的,但还有新思想在里面

我们仍是设 \(n\) 个函数,对于第 \(i\) 个函数,我们设 \(F_i[a_i]=x,\ F_i[b_i]=y,\ F_i[c_i]=z\)

我们考虑将它们卷起来,得到第 \(i\) 位上的系数就是答案为 \(i\) 的值

一样套路,因为只有 \(a_i,b_i,c_i\) 上有值,所以转化为

\[FWT(F_i)[k]=(-1)^{pop\_cnt(k\&a_i)}x+(-1)^{pop\_cnt(k\&b_i)}y+(-1)^{pop\_cnt(k\&c_i)}z \]

类似上一题,我们这时候有 \(8\) 种取值

当然,这其实是可以做的,但实在是太麻烦了,我们考虑减少一些分类情况

我们考虑对每一对三元组 \((a_i,b_i,c_i)\),每个数都异或上 \(a_i\),也就是得到 \((0,b_i\oplus a_i, c_i\oplus a_i)\)

这样说明我们默认选择了 \(a_i\),但如果我们选择 \(b_i\oplus a_i\) 或者 \(c_i\oplus a_i\),就相当于选择 \(b_i,\ c_i\),这与原问题等价(最后输出第 \(i\) 个答案时应为 \(ans[i\oplus sum]\),其中 \(sum=a_1\oplus a_2\oplus ...\ a_n\)

这样 \(x\) 前面 的取值就为 \(1\)

就有 \(4\) 种情况:

\[x+y+z \]

\[x+y-z \]

\[x-y+z \]

\[x-y-z \]

\(FWT(S)[k]=\prod_{i=1}^n FWT(F_i)[k]\),每种取值各有 \(c_1,c_2,c_3,c_4\) 个(\(S\) 就是最终答案)

那么首先应有:

\[c_1+c_2+c_3+c_4=n \]

我们考虑再构造出 \(3\) 个等式

  • 我们设 \(n\) 个函数,第 \(i\) 个函数 \(G_i[b_i]=1\),也就是 \(x=0,y=1,z=0\)

我们考虑将求 \(FWT(G_i)\),就有 \(FWT(G_i)[k]=(-1)^{pop\_cnt(k\&b_i)}\)

我们考虑求 \(FWT(\sum_{i=1}^n G_i)\),那么其实有 \(FWT(\sum_{i=1}^n G_i)[k]=\sum_{i=1}^n (-1)^{pop\_cnt(k\&b_i)}\),设为 \(p_1\)

就能得到 \(p_1=c_1+c_2-c_3-c_4\)

  • 同理,我们再设 \(n\) 个函数,第 \(i\) 个函数 \(G_i[c_i]=1\),也就是 \(x=0,y=0,z=1\)

也就有 \(FWT(\sum_{i=1}^n G_i)[k]=\sum_{i=1}^n (-1)^{pop\_cnt(k\&c_i)}\),设为 \(p_2\)

\(p_2=c_1-c_2+c_3-c_4\)

  • 我们再构造 \(n\) 个函数,第 \(i\) 个函数 \(G_i[b_i\oplus c_i]=1\),也就是将我们前面设的式子,第 \(i\)对着卷上\(i\)

就有 \(FWT(\sum_{i=1}^n G_i)[k]=\sum_{i=1}^n (-1)^{pop\_cnt(k\&b_i)}(-1)^{pop\_cnt(k\&c_i)}\),设为 \(p_3\)

\(p_3=c_1-c_2-c_3+c_4\)

这样我们就得到了:

\[\begin{cases} c_1+c_2+c_3+c_4=n \\ c_1+c_2-c_3-c_4=p_1 \\ c_1-c_2+c_3-c_4=p_2 \\ c_1-c_2-c_3+c_4=p_3 \end{cases}\]

解方程就能得到

\[\begin{cases} c_1=(n+p_1+p_2+p_3)/4 \\ c_2=(n+p_1-2c_1)/2 \\ c_3=(n+p_2-2c_1)/2 \\ c_4=(n+p_3-2c_1)/2 \end{cases}\]

最后我们再按照上一题,求幂,IFWT 就可以求出答案了!

代码

posted @ 2022-06-02 15:12  zuytong  阅读(135)  评论(0编辑  收藏  举报