子集卷积

两个集合幂级数做卷积的时候可以这样:

对于原来的 \(a_i\) ,将其变为 \(a_ix^{\text{popcount}(i)}\) 。然后对两个做或卷积,最后卷积之后 \(s\) 的值就是 \([x^{\text{popcount}(s)}]\) 。可以理解为 \(x^i\) 就是表示有 \(i\)\(1\) ,然后或卷积可以得到 \(a(1)+b(1)\leq c(1)\) ,并且只有 \(a(1)+b(1)=c(1)\) 才是有效的。

对于更高级的,比如求 \(\ln,\exp\) 或者快速幂、求导之类的都可以这样做。

板子:

#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+9;
void add(int &x,int y){if ((x+=y)>=mod) x-=mod;}
void sub(int &x,int y){if ((x-=y)<0) x+=mod;}
int a[1<<20][21],b[1<<20][21],c[1<<20][21],n;
void add(int* a,int* b,int n){for (int i=0;i<=n;++i) add(a[i],b[i]);}
void sub(int* a,int* b,int n){for (int i=0;i<=n;++i) sub(a[i],b[i]);}
void FWT(int a[1<<20][21],int n,int op){
	for (int w=0;w<n;++w)
		for (int s=0;s<(1<<n);s+=(1<<w+1))
			for (int t=0;t<(1<<w);++t)
				op==1?add(a[s|t|(1<<w)],a[s|t],n):sub(a[s|t|(1<<w)],a[s|t],n);
}
int main(){
//	freopen("a.in","r",stdin);
	scanf("%d",&n);
	for (int i=0;i<(1<<n);++i) scanf("%d",&a[i][__builtin_popcount(i)]);
	for (int i=0;i<(1<<n);++i) scanf("%d",&b[i][__builtin_popcount(i)]);
	FWT(a,n,1);
	FWT(b,n,1);
	for (int s=0;s<(1<<n);++s)
		for (int i=0;i<=n;++i)
			for (int j=0;j<=i;++j)
				add(c[s][i],1ll*a[s][j]*b[s][i-j]%mod);
	FWT(c,n,-1);
	for (int s=0;s<(1<<n);++s) printf("%d ",c[s][__builtin_popcount(s)]);
	return 0;
}
posted @ 2023-06-16 21:15  ruizhangj  阅读(113)  评论(0)    收藏  举报