【洛谷6097】【模板】子集卷积
大致题意: 给定\(a_{0\sim 2^n-1},b_{0\sim 2^n-1}\),求\(c_{0\sim 2^n-1}\)满足\(c_k=\sum_{i|j=k,i\&j=0}a_ib_j\)。
子集卷积
做这个之前,要先了解\(FWT\)。
考虑只要用\(FWT\)做或卷积,就可以轻松满足\(i|j=k\)这一限制,可要同时满足\(i\&j=0\),似乎没法直接搞。
但是,稍加分析我们就会发现,这两个限制放在一起其实会产生一个奇妙的性质。
因为\(i\&j=0\),所以\(i\)和\(j\)不可能在同一位上存在\(1\),而\(i|j=k\),则\(k\)的每一位上的\(1\)应该被恰好分给\(i\)和\(j\)的其中一个。
换言之,\(i\)和\(j\)二进制下\(1\)的个数之和等于\(k\)二进制下\(1\)的个数。
于是,我们定义\(f_{i,j},g_{i,j}\)表示:(\(cnt(j)\)为\(j\)二进制下\(1\)的个数)
\[f_{i,j}=\begin{cases}a_j&i=cnt(j)\\0&i\not=cnt(j)\end{cases},g_{i,j}=\begin{cases}b_j&i=cnt(j)\\0&i\not=cnt(j)\end{cases}
\]
然后我们令,\(ans_i=\sum_{j=0}^if_j*g_{i-j}\),则\(c_i=ans_{cnt(i),i}\)。
注意,这里的\(*\)为\(FWT\)的或卷积,因此上式可以看作是卷积套卷积,只不过外层这个卷积\(n\le20\),完全无需优化而已。
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 20
#define X 1000000009
using namespace std;
int n,P,a[N+5][1<<N],b[N+5][1<<N],ans[N+5][1<<N],g[1<<N];
class FastIO
{
private:
#define FS 100000
#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
#define pc(c) (C==E&&(clear(),0),*C++=c)
#define D isdigit(c=tc())
int T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS];
public:
I FastIO() {A=B=FI,C=FO,E=FO+FS;}
Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
Tp I void write(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);pc(' ');}
I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
}F;
I void FWT(int *s,CI op)//FWT或卷积
{
RI i,j,k;for(i=1;i^P;i<<=1) for(j=0;j^P;j+=i<<1)
for(k=0;k^i;++k) s[i+j+k]=(1LL*op*s[j+k]+s[i+j+k])%X;
}
int main()
{
RI i,j,k;for(F.read(n),P=1<<n,i=0;i^P;++i) g[i]=g[i>>1]+(i&1);//预处理每个数二进制下1的个数
for(i=0;i^P;++i) F.read(a[g[i]][i]);for(i=0;i^P;++i) F.read(b[g[i]][i]);//读入
for(i=0;i<=n;++i) FWT(a[i],1),FWT(b[i],1);for(i=0;i^P;++i)//先做DWT
for(j=0;j<=n;++j) for(k=0;k<=j;++k) ans[j][i]=(1LL*a[k][i]*b[j-k][i]+ans[j][i])%X;//求出ans数组
for(i=0;i<=n;++i) FWT(ans[i],X-1);for(i=0;i^P;++i) F.write(ans[g[i]][i]);return F.clear(),0;//做IDWT,输出答案
}
待到再迷茫时回头望,所有脚印会发出光芒