FWT 学习笔记

解决的问题

\(\rm FWT\) 是用来解决位运算卷积的。

啥是位运算卷积呢?

常见的多项式乘法可以认为是一种加法卷积,即 \(A_{i+j}=\sum B_i \times C_j\)

位运算卷积就是 \(A_{i \ \text{Or/And/Xor} \ j}=\sum B_i \times C_j\)

主要思想

现在以异或卷积为例,默认 \(n=2^k\)

回忆 \(\rm FFT/NTT\) 的过程,最重要的一步就是把多项式转成点值表示,然后卷积就变成了点积。

所以,我们希望 \(\rm FWT\) 也能把卷积转成点积。

\(\text{FWT}(A)\) 表示幂级数 \(A\) 通过 \(\rm FWT\) 转化成的幂级数。

我们想让 \(\rm FWT\) 满足若 \(A * B = C\),则

\[\text{FWT}(A) ⋅ \text{FWT}(B)=\text{FWT}(C) \]

\(c(i,j)\)\(A_j\) 贡献到 \(\text{FWT}(A)_j\) 的系数。

那么有 \(\text{FWT}(A)_i = \sum A_j \times c(i,j)\)

带入原式,得到:

\[\sum A_j \times c(i,j) \times \sum B_k \times c(i,k) = \sum C_p \times c(i,p) \]

\[\sum \sum c(i,j)c(i,k)A_jB_k=\sum C_p \times c(i,p) \]

又由于 \(A * B = C\),所以:

\[C_p = \sum_{t1 \oplus t2=p} A_{t1} \times B_{t2} \]

\[\sum \sum c(i,j)c(i,k)A_jB_k=\sum_{p=0}^{n-1} c(i,p) \times \sum_{t1 \oplus t2=p} A_{t1} \times B_{t2} \]

又因为:

\[\sum \sum c(i,j)c(i,k)A_jB_k=\sum_{p=0}^{n-1} \sum_{t1 \oplus t2=p} c(i,t1)c(i,t2)A_{t1}B_{t2}=\sum_{p=0}^{n-1} \sum_{t1 \oplus t2=p} c(i,t1) \times c(i,t2) \sum_{t1 \oplus t2=p} A_{t1} \times B_{t2} \]

然后就得到:

\[\sum_{p=0}^{n-1} c(i,p) \times \sum_{t1 \oplus t2=p} A_{t1} \times B_{t2}=\sum_{p=0}^{n-1} \sum_{t1\oplus t2=p} c(i,t1) \times c(i,t2) \sum_{t1 \oplus t2=p} A_{t1} \times B_{t2} \]

所以 \(c\) 只需满足 \(c(i,j) \times c(i,k)=c(i,j \oplus k)\)

又因为位运算的独立性,所以 \(c(i,j)\) 可以分位考虑。

\(d(0/1,0/1)\) 表示某种满足要求的变换。

那么把每一位乘起来,设 \(x_i\) 表示 \(x\) 的二进制第 \(i\) 位,得到

\[c(i,j) = d(i_0,j_0) \times d(i_1,j_1) \times ... \]

实现

假设我们已经求出 \(c\)\(d\),那么我们现在需要快速求解。

\[\text{FWT}(A)_i=\sum c(i,j) \times A_j \]

那么根据位运算的优秀性质,设 \(x'\) 表示 \(x\) 去掉二进制首位后变成的数,我们可以从中间劈开来,得到:

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

\[\text{FWT}(A)_i=\sum_{j=0}^{\frac{n}{2}-1} d(i_0,j_0) \times c(i,j) \times A_j + \sum_{j=\frac{n}{2}}^{n-1} d(i_0,j_0) \times c(i,j) \times A_j \]

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

\(A_0\) 为幂级数下标首位为零的部分,\(A_1\) 为幂级数下标首位为一的部分。

如果 \(i_0=0\),那么有:

\[\text{FWT}(A)_i=d(0,0) \text{FWT}(A_0)_i + d(0,1) \text{FWT}(A_1)_i \]

如果 \(i_0=1\),那么有:

\[\text{FWT}(A)_i=d(1,0) \text{FWT}(A_0)_i + d(1,1) \text{FWT}(A_1)_i \]

这里 \(\text{FWT}(A_{0/1})_i\) \(i\) 的范围都是 \([0,\frac{n}{2})\)

这样我们就可以 \(\mathcal O(n)\) 合并 \(A_0\)\(A_1\) 了,而 \(A_0\)\(A_1\) 的规模都是原问题的一半,所以复杂度为 \(\mathcal O(n \log n)\)

\(\rm FWT\) 逆变换就相当于矩阵求逆。

inline void FWT(mint *f,const mint d[2][2],int n){
	for (int len=1;len<n;len<<=1)
		for (int p=0;p<n;p+=len+len)
			for (int i=p;i<p+len;++i){
				mint A=f[i],B=f[i+len];
				f[i]=d[0][0]*A+d[0][1]*B;
				f[i+len]=d[1][0]*A+d[1][1]*B;
			}
}
inline void get(mint *a,mint *b,const mint d[2][2],const mint id[2][2]){
	FWT(a,d,n),FWT(b,d,n);
	for (int i=0;i<n;++i) a[i]*=b[i];
	FWT(a,id,n);
}

求解 \(d\) 矩阵

根据定义直接硬推,过程不放了,放几个结论。

\(\rm Or\) 运算的矩阵为:\(\left[\begin{matrix}1 & 0\\1 & 1\end{matrix}\right]\),逆矩阵为:\(\left[\begin{matrix}1 & 0\\-1 & 1\end{matrix}\right]\)
\(\rm And\) 运算的矩阵为:\(\left[\begin{matrix}1 & 1\\0 & 1\end{matrix}\right]\),逆矩阵为:\(\left[\begin{matrix}1 & -1\\0 & 1\end{matrix}\right]\)
\(\rm Xor\) 运算的矩阵为:\(\left[\begin{matrix}1 & 1\\1 & -1\end{matrix}\right]\),逆矩阵为:\(\left[\begin{matrix}0.5 & 0.5\\0.5 & -0.5\end{matrix}\right]\)

一些题目

Triple

简要题意:有 \(n\) 个长度为 \(2^k\) 的数组,满足每个数组只有三个位置有值,且每个数组这三个位置的值组成的可重集都是一样的,求 \(n\) 个数组的异或卷积。

【模板】子集卷积

简要题意:求 \(C\) 数组,满足 \(C_i = \sum_{j \oplus k=i} A_j \times B_k\)


参考资料

cmd 博客

posted @ 2023-12-24 16:28  Tx_Lcy  阅读(21)  评论(0)    收藏  举报