子集卷积
两个集合幂级数做卷积的时候可以这样:
对于原来的 \(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;
}

浙公网安备 33010602011771号