位运算卷积与集合幂级数

位运算卷积:

定义位运算卷积:

\(i\) 项和第 \(j\) 项的乘积贡献到第 \(i⊕j\) 项。其中 \(⊕\) 是某种位运算,即:

\[S[k]=\sum_{i⊕j=k}A[i]⋅B[j] \]

记作:

\[S=A*B \]

构造 \(FWT\) 变换:

尝试把位运算卷积转化成点积。

\(fwt(A)\) 是幂级数 \(A\) 经过 \(FWT\) 变换之后得到的幂级数,其满足:

\[A*B=C⟺fwt(A)⋅fwt(B)=fwt(C) \]

我们希望构造出来的是一个线性变换,设:

\[fwt(A)[i]=\sum_{j=0}^{n-1}c(i,j)⋅A[j] \]

有:

\[fwt(S)[i]=fwt(A)[i]⋅fwt(B)[i] \]

\[\sum_{j=0}^{n-1}c(i,j)⋅S[j]=\sum_{k=0}^{n-1}c(i,k)⋅A[k]⋅\sum_{p=0}^{n-1}c(i,p)⋅B[p] \]

\[\sum_{j=0}^{n-1}c(i,j)⋅S[j]=\sum_{k=0}^{n-1}\sum_{p=0}^{n-1}c(i,k)⋅c(i,p)⋅A[k]⋅B[p] \]

\(S=A*B\) 有:

\[S[p]=\sum_{j⊕k=p}A[j]⋅B[k] \]

\[\sum_{p=0}^{n-1}c(i,p)⋅S[p]=\sum_{p=0}^{n-1}c(i,p)⋅\sum_{j⊕k=p}A[j]⋅B[k] \]

\[\sum_{p=0}^{n-1}c(i,p)⋅S[p]=\sum_{p=0}^{n-1}\sum_{j⊕k=p}c(i,p)⋅A[j]⋅B[k] \]

\[=\sum_{p=0}^{n-1}\sum_{j⊕k=p}c(i,j⊕k)⋅A[j]⋅B[k] \]

\[=\sum_{i=0}^{n-1}\sum_{k=0}^{n-1}c(i,j⊕k)⋅A[j]⋅B[k] \]

\(\sum_{j=0}^{n-1}\limits c(i,j)⋅S[j]=\sum_{k=0}^{n-1}\limits \sum_{p=0}^{n-1}\limits c(i,k)⋅c(i,p)⋅A[k]⋅B[p]\) 得:

\[\sum_{j=0}^{n-1}\sum_{k=0}^{n-1}c(i,j)⋅c(i,k)⋅A[j]⋅B[k]=\sum_{j=0}^{n-1}\sum_{k=0}^{n-1}c(i,j⊕k)⋅A[j]⋅B[k] \]

因此,只需有:

\[c(i,j)⋅c(i,k)=c(i,j⊕k) \]

另外,由于位运算每一位的独立性,有:

\[c(i,j)=\prod_{k=0}^{log\ n}c(i_k,j_k) \]

因此:

\[c(i,j)⋅c(i,k)=c(i,j⊕k)⟺c(i_t,j_t)⋅c(i_t,k_t)=c(i_t,j_t⊕k_t),\forall t\in[0,log\ n] \]

因此我们只要针对不同的位运算构造出一个 \(2*2\) 的矩阵即可

1. \(Or\) 卷积

设:

\[c=\left[ \begin{matrix} {c(0,0)}&{c(0,1)}\\ {c(1,0)}&{c(1,1)}\\ \end{matrix} \right ]\]

有:

\[\begin{cases} \ \ c(0,0)⋅c(0,0)=c(0,0),&c(0,0)⋅c(0,1)=c(0,1)\\ \\ \ \ c(0,1)⋅c(0,0)=c(0,1),&c(0,1)⋅c(0,1)=c(0,1)\\ \\ \ \ c(1,0)⋅c(1,0)=c(1,0),&c(1,0)⋅c(1,1)=c(1,1)\\ \\ \ \ c(1,1)⋅c(1,0)=c(1,1),&c(1,1)⋅c(1,1)=c(1,1)\\ \end{cases} \]

为了方便在变换后还原,矩阵必须要有逆,因此,最终答案有两个:

\[\begin{cases} c_1=\left[ \begin{matrix} {1}&{1}\\ {1}&{0}\\ \end{matrix} \right ],&& c_2=\left[ \begin{matrix} {1}&{0}\\ {1}&{1}\\ \end{matrix} \right ] \end{cases} \]

对于第二个矩阵,显然有 \(c(i,j)=[j\)&\(i=j]\) 因此我们使用第二个矩阵。

还原时对矩阵求逆即可:

\[c^T=\left[ \begin{matrix} {1}&{0}\\ {-1}&{1}\\ \end{matrix} \right ]\]

2. \(And\) 卷积

设:

\[c=\left[ \begin{matrix} {c(0,0)}&{c(0,1)}\\ {c(1,0)}&{c(1,1)}\\ \end{matrix} \right ]\]

有:

\[\begin{cases} \ \ c(0,0)⋅c(0,0)=c(0,0),&c(0,0)⋅c(0,1)=c(0,0)\\ \\ \ \ c(0,1)⋅c(0,0)=c(0,0),&c(0,1)⋅c(0,1)=c(0,1)\\ \\ \ \ c(1,0)⋅c(1,0)=c(1,0),&c(1,0)⋅c(1,1)=c(1,0)\\ \\ \ \ c(1,1)⋅c(1,0)=c(1,0),&c(1,1)⋅c(1,1)=c(1,1)\\ \end{cases} \]

最终答案有两个:

\[\begin{cases} c_1=\left[ \begin{matrix} {0}&{1}\\ {1}&{1}\\ \end{matrix} \right ],&& c_2=\left[ \begin{matrix} {1}&{1}\\ {0}&{1}\\ \end{matrix} \right ] \end{cases} \]

对于第二个矩阵,显然有 \(c(i,j)=[j\)&\(i=i]\) 因此我们使用第二个矩阵。

还原时对矩阵求逆即可:

\[c^T=\left[ \begin{matrix} {1}&{-1}\\ {0}&{1}\\ \end{matrix} \right ]\]

3. \(Xor\) 卷积

设:

\[c=\left[ \begin{matrix} {c(0,0)}&{c(0,1)}\\ {c(1,0)}&{c(1,1)}\\ \end{matrix} \right ]\]

有:

\[\begin{cases} \ \ c(0,0)⋅c(0,0)=c(0,0),&c(0,0)⋅c(0,1)=c(0,1)\\ \\ \ \ c(0,1)⋅c(0,0)=c(0,1),&c(0,1)⋅c(0,1)=c(0,0)\\ \\ \ \ c(1,0)⋅c(1,0)=c(1,0),&c(1,0)⋅c(1,1)=c(1,1)\\ \\ \ \ c(1,1)⋅c(1,0)=c(1,1),&c(1,1)⋅c(1,1)=c(1,0)\\ \end{cases} \]

最终答案有四个:

\[\begin{cases} c_1=\left[ \begin{matrix} {1}&{1}\\ {-1}&{1}\\ \end{matrix} \right ],&& c_2=\left[ \begin{matrix} {1}&{1}\\ {1}&{-1}\\ \end{matrix} \right ],&& c_3=\left[ \begin{matrix} {-1}&{1}\\ {1}&{1}\\ \end{matrix} \right ],&& c_4=\left[ \begin{matrix} {1}&{-1}\\ {1}&{1}\\ \end{matrix} \right ] \end{cases} \]

对于第二个矩阵,显然有 \(c(i,j)=(-1)^{|i\& j|}\) 因此我们使用第二个矩阵。

还原时对矩阵求逆即可:

\[c^T=\left[ \begin{matrix} {0.5}&{0.5}\\ {0.5}&{-0.5}\\ \end{matrix} \right ]\]


子集卷积

求:

\[c_k=\sum_{i∩j=∅,i∪j=k}a_{i}\times b_{j} \]

若只考虑 \(i∪j=k\) ,一次 \(\text{or}\) 卷积即可,考虑 \(i∩j=∅\) 的限制,发现其等价于 \(|i|+|j|=|i\cup j|\)

因此再开一维记录集合中的元素个数即可。

时间复杂度 \(O(n^2 2^n)\)


\(FWT\) 变换的一些性质:

\(FWT\) 是线性变换:

\[\begin{cases} fwt(A+B)=fwt(A)+fwt(B)\\ \\ fwt(c⋅A)=c⋅fwt(A) \end{cases} \]

由定义 \(fwt(A)[i]=\sum_{j=0}^{n-1}\limits c(i,j)⋅A[j]\) 后显然可证。

  1. 求:

从序列 \(\{a_i\}\) 中选择一个非空子集,使子集内的数按位与起来为零

\[\]

solution 1:

\(dp[s]\) 表示按位与起来结果为 \(s\) 的超集的方案数

\(FWT\) 求出 \(dp[s]\) ,则有:

\[ans=\sum_{s=0}(-1)^{cnt[s]}dp[s] \]

solution 2:

\(dp[i][s]\) 表示到了第 \(i\) 位,运算结果位 \(s\) 的方案数,有:

\[dp[i][s]=dp[i-1][s]+\sum_{s=j\&a[i]}dp[i-1][j] \]

\[dp[i][s]=dp[i-1][s]+\sum_{p\&k=s}[p=a[i]]⋅dp[i-1][k] \]

对上式进行 \(fmt\) 变换,由于 \(fwt(A+B)=fwt(A)+fwt(B)\) 可知:

\[fwt(dp[i-1][s]+\sum_{p\&k=s}[p=a[i]]⋅dp[i-1][k])=fwt(dp[i-1][s])+fwt(\sum_{p\&k=s}[p=a[i]]⋅dp[i-1][k]) \]

显然:

\[fwt(\sum_{p\&k=s}[p=a[i]]⋅dp[i-1][k])=fwt([p=a[i]])⋅fwt(dp[i-1][p]) \]

因此:

\[fwt(dp[i][s])=fwt(dp[i-1][s])*(1+fwt([p=a[i]])) \]

\(cnt[s]=\sum_{i=1}^{n}\limits [s=a[i]]\)

\[fwt(dp[n][s])=2^{fwt(cnt[s])} \]

【模板】快速莫比乌斯/沃尔什变换 (FMT/FWT)

#include<bits/stdc++.h>
using namespace std;
int n;
int A[(1<<17)+5],B[(1<<17)+5];
int a[(1<<17)+5],b[(1<<17)+5],c[(1<<17)+5];
const int md=998244353;
inline void cpy(){
	for(int i=0;i<(1<<n);i++)a[i]=A[i];
	for(int i=0;i<(1<<n);i++)b[i]=B[i];
}
inline void solve(){
	for(int i=0;i<(1<<n);i++)c[i]=(1ll*a[i]*b[i])%md;
}
inline void output(){
	for(int i=0;i<(1<<n);i++)printf("%d ",(c[i]+md)%md);
	puts("");
}
inline void And(int *dp,int x){
	for(int i=0;i<n;i++){
		for(int s=0;s<(1<<n);s++){
			if((s>>i)&1)continue;
			dp[s]=(dp[s]+x*dp[s|(1<<i)])%md;
		}
	}
}
inline void Or(int *dp,int x){
	for(int i=0;i<n;i++){
		for(int s=0;s<(1<<n);s++){
			if((s>>i)&1)continue;
			dp[s|(1<<i)]=(dp[s|(1<<i)]+x*dp[s])%md;
		}
	}
}
inline void Xor(int *dp,int x){
	for(int i=0;i<n;i++){
		for(int s=0;s<(1<<n);s++){
			if((s>>i)&1)continue;
			long long u=dp[s],t=dp[s|(1<<i)];
			dp[s]=1ll*x*(u+t)%md;
			dp[s|(1<<i)]=1ll*x*(u-t)%md;
		}
	}
}
int main(){
	scanf("%d",&n);
	for(int i=0;i<(1<<n);i++)scanf("%d",&A[i]);
	for(int i=0;i<(1<<n);i++)scanf("%d",&B[i]);
	cpy();Or(a,1);Or(b,1);solve();Or(c,-1);output();
	cpy();And(a,1);And(b,1);solve();And(c,-1);output();
	cpy();Xor(a,1);Xor(b,1);solve();Xor(c,(md+1)>>1);output();

	return 0;
}



【模板】子集卷积

#include <bits/stdc++.h>
using namespace std;
int n;
int a[21][1<<20],b[21][1<<20],res[21][1<<20],cnt[1<<20];
const int md=1e9+9;
inline void fwt(int *dp,int x){
	for(int i=0;i<n;i++){
		for(int s=0;s<(1<<n);s++){
			if((s>>i)&1)continue;
			dp[s^(1<<i)]=(dp[s^(1<<i)]+x*dp[s])%md;
		}
	}
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<(1<<n);i++)cnt[i]=cnt[i>>1]+(i&1);
	for(int i=0;i<(1<<n);i++)scanf("%d",&a[cnt[i]][i]);
	for(int i=0;i<(1<<n);i++)scanf("%d",&b[cnt[i]][i]);
	for(int i=0;i<=n;i++)fwt(a[i],1);
	for(int i=0;i<=n;i++)fwt(b[i],1);
	for(int i=0;i<=n;i++){
		for(int j=0;j+i<=n;j++){
			for(int s=0;s<(1<<n);s++){
				res[i+j][s]=(res[i+j][s]+1ll*a[i][s]*b[j][s])%md;
			}
		}
	}
	for(int i=0;i<=n;i++)fwt(res[i],-1);
	for(int i=0;i<(1<<n);i++)printf("%d ",(res[cnt[i]][i]+md)%md);

	return 0;
}


Jzzhu and Numbers

code 1:

#include<bits/stdc++.h>
using namespace std;
int n;
int dp[1<<20],f[1<<20],cnt[1<<20];
const int md=1e9+7;
inline int pwr(int x,int y){
	int res=1;
	while(y){
		if(y&1)res=1ll*res*x%md;
		x=1ll*x*x%md;y>>=1;
	}return res;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		int x;scanf("%d",&x);
		dp[x]++;
	}
	for(int i=0;i<20;i++){
		for(int s=0;s<(1<<20);s++){
			if((s>>i)&1)continue;
			dp[s]+=dp[s|(1<<i)];
		}
	}
	for(int i=0;i<(1<<20);i++)f[i]=pwr(2,dp[i])-1;
	long long ans=0;
	for(int i=0;i<(1<<20);i++){
		cnt[i]=cnt[i>>1]+(i&1);
		if(cnt[i]&1)ans=(ans-f[i])%md;
		else ans=(ans+f[i])%md;
	}
	printf("%lld",(ans+md)%md);

	return 0;
}


code 2:

#include<bits/stdc++.h>
using namespace std;
int n;
int dp[1<<20],f[1<<20],cnt[1<<20];
const int md=1e9+7;
inline int pwr(int x,int y){
	int res=1;
	while(y){
		if(y&1)res=1ll*res*x%md;
		x=1ll*x*x%md;y>>=1;
	}return res;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		int x;scanf("%d",&x);
		dp[x]++;
	}
	for(int i=0;i<20;i++){
		for(int s=0;s<(1<<20);s++){
			if((s>>i)&1)continue;
			dp[s]+=dp[s|(1<<i)];
		}
	}
	for(int i=0;i<(1<<20);i++)dp[i]=pwr(2,dp[i]);
	for(int i=0;i<20;i++){
		for(int s=0;s<(1<<20);s++){
			if((s>>i)&1)continue;
			dp[s]=(dp[s]-dp[s|(1<<i)])%md;
		}
	}
	printf("%d",(dp[0]+md)%md);

	return 0;
}


posted @ 2021-12-14 06:51  一粒夸克  阅读(107)  评论(0编辑  收藏  举报