位运算卷积学习笔记

位运算卷积学习笔记

位运算卷积,即快速沃尔什变换 \(\text{FWT}\) 和快速莫比乌斯变换 \(\text{FMT}\),但事实上最常用的是 \(\text{FWT}\),因为 \(\text{FMT}\) 所求解的内容是 \(\text{FWT}\) 的子集。

位运算卷积

首先要知道位运算卷积指的是

\[c_i=\sum_{j\odot k=i}a_jb_k \]

形式的式子,其中 \(\odot\) 代指了任意位运算符,也就是 \(\text{or},\text{and},\text{xor}\)。利用 \(\text{FMT}\) 能够求解 \(\text{or},\text{and}\) 卷积,而利用 \(\text{FWT}\) 能够求解 \(\text{or},\text{and},\text{xor}\) 卷积。

快速莫比乌斯变换

\(\text{or}\) 卷积

先考虑暴力求解的时间复杂度是 \(O(n^2)\) 的,不妨考虑和 \(\text{FFT}\) 相同的思路,找到一个 \(\text{FMT}(a)\) 满足 \(\text{FMT}(c)_i=\text{FMT}(a)_i\times\text{FMT}(b)_i\),这样我们便可以通过顺逆变换快速求出 \(c\)

定义 \(\text{FMT}(a)_i=\sum_{j\cup i=i}a_j\),其中 \(\cup\) 表示按位或,而这种构造显然符合要求,因为有

\[\begin{aligned}\text{FMT}(a)_i\times\text{FMT}(b)_i&=\sum_{j\cup i=i}a_j\sum_{k\cup i=i}b_k\\&=\sum_{(j\cup k)\cup i=i}a_jb_k\\&=\text{FMT}(c)_i \end{aligned} \]

由此我们得到了一个构造,而且不难发现这其实就是 \(a\) 的高维前缀和,按照前缀和的思路将 \(n\) 维二进制看作 \(n\) 维数组求解前缀和即可。而对于逆变换只需要进行差分即可。时间复杂度是 \(O(n\log n)\) 的。

代码

int n;
int A[1<<n],B[1<<n],C[1<<n];

void FMT_OR(int n,int *a,int type){
	for(int i=0;i<n;i++){
		for(int j=0;j<(1<<n);j++){
			if(j&(1<<i))a[j]+=type*a[j^(1<<i)];
		}
	}
	return ;
}

int main(){
	cin>>n;
	for(int i=0;i<(1<<n);i++)cin>>A[i];
	for(int i=0;i<(1<<n);i++)cin>>B[i];
	FMT_OR(n,A,1);
	FMT_OR(n,B,1);
	for(int i=0;i<(1<<n);i++)C[i]=A[i]*B[i];
	FMT_OR(n,C,-1);
	for(int i=0;i<(1<<n);i++)cout<<C[i]<<" ";
	return 0;
}

\(\text{and}\) 卷积

\(\text{or}\) 卷积同理,考虑构造一个 \(\text{FMT}(a)_i\),在这里定义 \(\text{FMT}(a)_i=\sum_{j\cap i=i}a_j\)\(\cap\) 表示按位与,那么有

\[\begin{aligned}\text{FMT}(a)_i\times \text{FMT}(b)_i&=\sum_{j\cap i=i}a_j\sum_{k\cap i=i}b_k\\&=\sum_{(j\cap k)\cap i=i}a_jb_k\\&=\text{FMT}(c)_i \end{aligned} \]

于是考虑如何求解 \(\text{FMT}(a)_i\),发现其为高位后缀和的形式,于是有复杂度 \(O(n\log n)\)

代码

int n;
int A[1<<n],B[1<<n],C[1<<n];

void FMT_AND(int n,int *a,int type){
	for(int i=0;i<n;i++){
		for(int j=0;j<(1<<n);j++){
			if(j&(1<<i))a[j^(1<<i)]+=type*a[j];
		}
	}
	return ;
}

int main(){
	cin>>n;
	for(int i=0;i<(1<<n);i++)cin>>A[i];
	for(int i=0;i<(1<<n);i++)cin>>B[i];
	FMT_AND(n,A,1);
	FMT_AND(n,B,1);
	for(int i=0;i<(1<<n);i++)C[i]=A[i]*B[i];
	FMT_AND(n,C,-1);
	for(int i=0;i<(1<<n);i++)cout<<C[i]<<" ";
	return 0;
}

快速沃尔什变换

\(\text{or}\) 变换

其实有关于 \(\text{or},\text{and}\)\(\text{FWT}\)\(\text{FMT}\) 的构造完全相同,只是求解的方法不同而已,我们在这里考虑分治的做法,也就是更接近 \(\text{FFT}\) 的做法。依旧构造 \(\text{FWT}(a)_i=\sum_{j\cup i=i}a_j\),正确性不加复述,下面考虑分治。对于分治序列 \(\text{FWT}(a)\) 的第 \(i\) 位,我们将其分为两部分 \(\text{FWT}(a)_0,\text{FWT}(a)_1\),分别表示在 \(i-1\) 位分治后序列左半部分和右半部分,于是有

\[\text{FWT}(a)=\text{merge}(\text{FWT}(a)_0,\text{FWT}(a)_0+\text{FWT}(a)_1) \]

这个正确性是显然的,因为对于 \(\text{FWT}(a)_0\) 来说,其分治位置上均为 \(0\),所需要累加的对应位置 \(j\) 若要满足 \(j\cup i=i\) 则其当前分治位也需为 \(0\)。而对于 \(\text{FWT}(a)_1\) 来说,\(j\) 的当前分治位即可以为 \(1\),也可以为 \(0\),因此只需要将后半段加上前半段即可。

关于逆变换则恰相反,只需要将前半段对后半段的贡献消去即可,也就是在后半段减去前半段。也就是

\[a=\text{merge}(a_0,a_1-a_0) \]

代码

int n;
int A[1<<n],B[1<<n],C[1<<n];

void FWT_OR(int n,int *a,int type){
	for(int len=1;len<n;len<<=1){
		for(int l=0;l<n;l+=(len<<1)){
			for(int k=0;k<len;k++)a[l+len+k]+=type*a[l+k];
		}
	}
	return ;
}

int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin>>n;
	for(int i=0;i<(1<<n);i++)cin>>A[i];
	for(int i=0;i<(1<<n);i++)cin>>B[i];
	FWT_OR(1<<n,A,1);
	FWT_OR(1<<n,B,1);
	for(int i=0;i<(1<<n);i++)C[i]=A[i]*B[i];
	FWT_OR(1<<n,C,-1);
	for(int i=0;i<(1<<n);i++)cout<<C[i]<<" ";
	return 0;
}

\(\text{and}\) 卷积

依旧同理有 \(\text{FWT}(a)_i=\sum_{j\cap i=i}a_j\),正确性不加复述,考虑分治的做法。对于分治序列 \(\text{FWT}(a)\) 的第 \(i\) 位,我们将其分为两部分 \(\text{FWT}(a)_0,\text{FWT}(a)_1\),分别表示在 \(i-1\) 位分治后序列左半部分和右半部分,于是有

\[\text{FWT}(a)=\text{merge}(\text{FWT}(a)_0+\text{FWT}(a)_1,\text{FWT}(a)_1) \]

同理,当第 \(i\) 位为 \(0\) 时,\(j\) 的当前分治位即可以是 \(0\) 也可以是 \(1\),而当第 \(i\) 位为 \(1\) 时,\(j\) 的当前分治位只能是 \(1\),逆变换同理减回去即可。

\[a=\text{merge}(a_0-a_1,a_1) \]

代码

int n;
int A[1<<n],B[1<<n],C[1<<n];

void FWT_AND(int n,int *a,int type){
	for(int len=1;len<n;len<<=1){
		for(int l=0;l<n;l+=(len<<1)){
			for(int k=0;k<len;k++)a[l+k]+=type*a[l+len+k];
		}
	}
	return ;
}

int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin>>n;
	for(int i=0;i<(1<<n);i++)cin>>A[i];
	for(int i=0;i<(1<<n);i++)cin>>B[i];
	FWT_AND(1<<n,A,1);
	FWT_AND(1<<n,B,1);
	for(int i=0;i<(1<<n);i++)C[i]=A[i]*B[i];
	FWT_AND(1<<n,C,-1);
	for(int i=0;i<(1<<n);i++)cout<<C[i]<<" ";
	return 0;
}

\(\text{xor}\) 卷积

引入一个新的运算符 \(\circ\)。定义 \(x\circ y=\text{popcnt}(x\cap y)\bmod 2\),其中 \(\text{popcnt}(x)\) 表示 \(x\) 二进制下 \(1\) 的个数。我们发现它满足 \((x\circ y)\oplus(x\circ z)=x\circ (y\oplus z)\)

更感性的理解,\(x\circ y\) 表示 \(x,y\) 均为 \(1\) 位数的奇偶性。对于每一位的 \(x,y,z\),可以列出下表

\(x\) \(y\) \(z\) \(y\oplus z\) \(x\circ y\) \(x\circ z\) \(x\circ (y\oplus z)\)
\(0\) \(0\) \(0\) \(0\) \(0\) \(0\) \(0\)
\(0\) \(0\) \(1\) \(1\) \(0\) \(0\) \(0\)
\(0\) \(1\) \(0\) \(1\) \(0\) \(0\) \(0\)
\(0\) \(1\) \(1\) \(0\) \(0\) \(0\) \(0\)
\(1\) \(0\) \(0\) \(0\) \(0\) \(0\) \(0\)
\(1\) \(0\) \(1\) \(1\) \(0\) \(1\) \(1\)
\(1\) \(1\) \(0\) \(1\) \(1\) \(0\) \(1\)
\(1\) \(1\) \(1\) \(0\) \(1\) \(1\) \(0\)

不难看出,不管 \(x,y,z\) 的每一位如何变化,对应位对答案贡献的奇偶性始终不变,故上式成立。

由此,设 \(\text{FWT}(a)_i=\sum_{j\circ i=0}a_j-\sum_{j\circ i=1}a_j\)。则有

\[\begin{aligned}\text{FWT}(a)_i\times\text{FWT}(b)_i&=(\sum_{j\circ i=0}a_j-\sum_{j\circ i=1}a_j)(\sum_{j\circ i=0}b_j-\sum_{j\circ i=1}b_j)\\&=(\sum_{j\circ i=0}a_j\sum_{k\circ i=0}b_k+\sum_{j\circ i=1}a_j\sum_{k\circ i=1}b_k)-(\sum_{j\circ i=0}a_j\sum_{k\circ i=1}b_k+\sum_{j\circ i=1}a_j\sum_{k\circ i=0}b_k)\\&=\sum_{(j\oplus k)\circ i=0}a_jb_k-\sum_{(j\oplus k)\circ i=1}a_jb_k\\&=\text{FWT}(c)_i \end{aligned} \]

由此,该构造的正确性得证,接着考虑如何分治求解。当前位 \(i\)\(0\) 时,不论 \(j\)\(0,1\)\(i\circ j\) 均为 \(0\),那么 \(\text{FWT}(a)_0,\text{FWT}(a)_1\) 对该位的贡献均为正,而当 \(i\)\(1\) 时,如果 \(j\)\(0\),则对答案没有影响,但若取 \(1\),则应对原贡献取负。即

\[\text{FWT}(a)=\text{merge}(\text{FWT}(a)_0+\text{FWT}(a)_1,\text{FWT}(a)_0-\text{FWT}(a)_1) \]

而在逆变换时有

\[a=\text{merge}(\frac{a_0+a_1}{2},\frac{a_0-a_1}{2}) \]

于是对于传参稍作改动,逆变换时 \(\text{type}\)\(\frac{1}{2}\)

代码

int n;
int A[1<<n],B[1<<n],C[1<<n];

void FWT_XOR(int n,int *a,int type){
	for(int len=1;len<n;len<<=1){
		for(int l=0;l<n;l+=(len<<1)){
			for(int k=0;k<len;k++){
				ll A=a[l+k],B=a[l+len+k];
				a[l+k]=(A+B)/type;
				a[l+len+k]=(A-B)/type;
			}
		}
	}
	return ;
}

int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin>>n;
	for(int i=0;i<(1<<n);i++)cin>>A[i];
	for(int i=0;i<(1<<n);i++)cin>>B[i];
	FWT_XOR(1<<n,A,1);
	FWT_XOR(1<<n,B,1);
	for(int i=0;i<(1<<n);i++)C[i]=A[i]*B[i];
	FWT_XOR(1<<n,C,2);
	for(int i=0;i<(1<<n);i++)cout<<C[i]<<" ";
	return 0;
}

拓展知识

矩阵角度的 \(\text{FWT}\)

假设 \(c(i,j)\)\(a_j\)\(\text{FWT}(a)_i\) 的贡献系数,我们可以重新描述 \(\text{FWT}\) 变换的过程

\[\text{FWT}(a)_i=\sum_{j=0}^{n-1}c(i,j)a_j \]

因为 \(\text{FWT}(a)_i\times \text{FWT}(b)_i=\text{FWT}(c)_i\),所以有

\[\begin{aligned}\text{FWT}(a)_i\times\text{FWT}(b)_i&=\sum_{j=0}^{n-1}c(i,j)a_j\sum_{k=0}^{n-1}c(i,k)b_k\\&=\sum_{j=0}^{n-1}\sum_{k=0}^{n-1}c(i,j)c(i,k)a_jb_k\\&=\sum_{j\odot k=0}^{n-1}c(i,j\odot k)a_jb_k\\&=\text{FWT}(c)_i \end{aligned} \]

所以应当有 \(c(i,j)c(i,k)=c(i,j\odot k)\)。同时 \(c\) 函数还应当能够按位处理。也就是我们将变换的形式

\[\text{FWT}(a)_i=\sum_{j=0}^{n-1}c(i,j)a_j \]

拆分成

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

考虑到式子分出的 \(j\) 只有最高位不同,所以我们将 \(i,j\) 出去最高位的值为 \(i',j'\),并且记 \(i_0\)\(i\) 的最高位,则有

\[\text{FWT}(a)_i=c(i_0,0)\sum_{j=0}^{\frac{n}{2}-1}c(i',j')a_j+c(i_0,1)\sum_{j=\frac{n}{2}}^{n-1}c(i',j')a_j \]

\(i_0=0\) 时,有

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

而当 \(i_0=1\) 时,则有

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

不难考虑分治维护 \(\sum_{j=0}^{\frac{n}{2}-1}c(i',j')a_j\)\(\sum_{j=\frac{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} \]

进行,我们称这个矩阵为位矩阵。而如果需要进行逆变换,则需要上面位矩阵的逆矩阵。即可以通过

\[a_i=\sum_{j=0}^{n-1}c^{-1}(i,j)\text{FWT}(a)_j \]

得到原序列。当然,逆矩阵不一定存在。当原矩阵存在一行或一列都是 \(0\) 时则该矩阵没有逆,需要在构造的时候注意。

\(\text{or}\) 卷积

不难构造出矩阵

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

满足 \(c(i,j)c(i,k)=c(i,j\cup k)\),不难发现,这等价于 \(\text{FWT}(a)=\text{merge}(\text{FWT}(a)_0,\text{FWT}(a)_0+\text{FWT}(a)_1)\)。而下面这个矩阵虽然也符合条件,但并不常见

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

而下面的矩阵虽然也符合 \(c(i,j)c(i,k)=c(i,j\cup k)\),但其没有逆,因此不合法

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

考虑最上方的矩阵的逆

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

这和逆变换的 \(a=\text{merge}(a_0,a_1-a_0)\) 等价。

\(\text{and}\) 卷积

同理构造

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

满足 \(c(i,j)c(i,k)=c(i,j\cap k)\),其逆矩阵为

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

\(\text{xor}\) 卷积

构造

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

满足 \(c(i,j)c(i,k)=c(i,j\oplus k)\),其逆矩阵为

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

\(\text{FWT}\) 的性质

\(\text{FWT}\) 是线性变换,这意味着

\[\text{FWT}(A+B)=\text{FWT}(A)+\text{FWT}(B)\\ \text{FWT}(cA)=c\text{FWT}(A) \]

\(K\)\(\text{FWT}\)

\(\text{max}\) 运算

重新审视 \(\cup\) 运算,发现它实际上是二进制下每一位取 \(\text{max}\)。如果将其拓展到 \(K\) 进制,有

\[c(i,j)c(i,k)=c(i,\max(j,k)) \]

\(j=k\) 时,上式转换成 \(c(i,j)^2=c(i,j)\),于是每一位只能取 \(1,0\),而每一行的 \(1\) 必须出现在 \(0\) 前,不然不符合要求,不难构造出

\[\begin{bmatrix}1&0&0&\cdots&0\\1&1&0&\cdots&0\\1&1&1&\cdots&0\\\vdots&\vdots&\vdots&\ddots&\vdots\\1&1&1&\cdots&1 \end{bmatrix} \]

其逆矩阵为

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

\(\text{min}\) 运算

重新审视 \(\cap\) 运算,发现它实际上是二进制下每一位取 \(\text{min}\)。如果将其拓展到 \(K\) 进制,有

\[c(i,j)c(i,k)=c(i,\min(j,k)) \]

\(j=k\) 时,上式转换成 \(c(i,j)^2=c(i,j)\),于是每一位只能取 \(1,0\),而每一行 \(1\) 后面不能有 \(0\),于是不难构造

\[\begin{bmatrix}1&1&1&\cdots&1\\0&1&1&\cdots&1\\0&0&1&\cdots&1\\\vdots&\vdots&\vdots&\ddots&\vdots\\0&0&0&\cdots&1\end{bmatrix} \]

其逆矩阵为

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

posted @ 2024-07-30 19:33  DycIsMyName  阅读(90)  评论(0)    收藏  举报