HDU - 6057 Kanade's convolution 题解
【分析】
考虑 \((i\vee j)=(i\wedge j)\vee (i\oplus j)\)
记 \(x=i\vee j,y=i\oplus j\Rightarrow x=y\vee k\Rightarrow k=x\oplus y(k,y\subseteq x)\)
\(\quad C_k\)
\(\displaystyle =\sum_{i\wedge j=k}A_{i\oplus j}B_{i\vee j}\)
\(\displaystyle =\sum_{x\oplus y=k}[y\subseteq x]A_yB_x\sum_i\sum_j[i\vee j=x][i \wedge j=k]\)
\(\displaystyle =\sum_{x\oplus y=k}[y\wedge x=y]A_yB_x2^{pc(x)-pc(k)}\)
其中 \(pc(n)\) 代表 \(n\) 二进制中 \(1\) 的个数
考虑到 \(k,y\) 均为 \(x\) 的子集,且 \(k=x\oplus y\)
故 \(pc(x)-pc(k)=pc(y)\) ,代入原式得到
\(\displaystyle C_k=\sum_{x\oplus y=k\\ y\wedge x=y}(A_y\cdot 2^{pc(y)})\cdot B_x\)
考虑到式子的本质,像是对于 \(k\) ,枚举另一个与它不交的集合 \(y\) ,把两者的并集和 \(y\) 集合的贡献统计到 \(k\) 上
类似的东西是普通的子集卷积: \(\displaystyle C_n=\sum_{i\vee j=n\\ i\wedge j=0}A_iB_j\)
这个代表的含义是:将集合 \(n\) 划分成两部分,将两个部分的贡献统计到 \(C_n\) 上
实现的方法在于:拓广一维,使得统计 \(i\) 对 \(n\) 的贡献时,强制使得统计的另一个贡献来源于 \((n-i)\)
由于子集卷积是对一个集合的划分,故可以得出:\(pc(n)=pc(i)+pc(n-i)\)
因此,考虑 \(i\) 的贡献时,强制其从 \(pc(i)\) 的维度转移过来,则可强制另一位从 \(pc(n-i)\) 转移;即可保证 \(i\) 与 \(j\) 不交
实现代码则如下:
inline int pc(int n) { return __builtin_popcount(n>>10)+__builtin_popcount(n&1023); }
inline void FST(int *a,int *b,int *c,int bits, int len){
for(int i=0;i<len;++i) A[pc(i)][i]=a[i];
for(int i=0;i<len;++i) B[pc(i)][i]=b[i];
for(int i=0;i<=bits;++i) FWT(A[i], len, 0), FWT(B[i], len, 0);
for(int x=0;x<=bits;++x)
for(int y=0;y<=x;++y)
for(int i=0;i<len;++i)
C[x][i]=add( C[x][i] , (ll)A[y][i]*B[x-y][i]%P );
for(int i=0;i<=bits;++i) FWT(C[i], len, 1);
for(int i=0;i<len;++i) c[i]=C[pc(i)][i];
}
总复杂度 \(O(n\log^2 n), n=2^bits=len\)
这题类似的处理
据说叫子集异或卷积,前面的叫子集或卷积
即考虑 \(A_y\) 的贡献从 \(y\) 转移时,强制从 \(pc(y)\) 转移,另一位 \(B_x\) 即可强制从 \(pc(k+y)\) 转移
inline int pc(int n) { return __builtin_popcount(n>>10)+__builtin_popcount(n&1023); }
inline void FST(int *a,int *b,int *c,int bits, int len){
for(int i=0;i<len;++i) A[pc(i)][i]=a[i];
for(int i=0;i<len;++i) B[pc(i)][i]=b[i];
for(int i=0;i<=bits;++i) FWT(A[i], len, 0), FWT(B[i], len, 0);
for(int x=0;x<=bits;++x)
for(int y=0;y<=x;++y)
for(int i=0;i<len;++i)
C[x-y][i]=add( C[x-y][i] , (ll)A[y][i]*B[x][i]%P );
for(int i=0;i<=bits;++i) FWT(C[i], len, 1);
for(int i=0;i<len;++i) c[i]=C[pc(i)][i];
}
复杂度相同
【代码】
最后上总代码,无封装
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MOD=998244353, M=1<<20;
int m, A[20][M], B[20][M], C[20][M];
inline int pc(int n) { return __builtin_popcount(n>>10)+__builtin_popcount(n&1023); }
inline int add(int a,int b) { return (a+b>=MOD)?(a+b-MOD):(a+b); }
inline int dis(int a,int b) { return (a-b<0)?(a-b+MOD):(a-b); }
inline int shr(int n,int p) { return p?((n&1)?(n+MOD>>1):(n>>1)):n; }
inline void FWT(int *a,int len,int op){
for(int k=0;1<<k<len;++k) for(int i=0;i<len;++i) if(~i>>k&1){
int j=i^(1<<k), x, y;
x=add(a[i], a[j]), y=dis(a[i], a[j]);
a[i]=shr(x,op), a[j]=shr(y,op);
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin>>m;
for(int i=0,n;!(i>>m);++i) cin>>A[pc(i)][i], A[pc(i)][i]=A[pc(i)][i]*(1ll<<pc(i))%MOD;
for(int i=0,n;!(i>>m);++i) cin>>B[pc(i)][i];
for(int i=0;i<=m;++i) FWT(A[i],1<<m,0), FWT(B[i],1<<m,0);
for(int x=0;x<=m;++x)
for(int y=0;y<=x;++y)
for(int i=0;!(i>>m);++i)
C[x-y][i]=add(C[x-y][i], (ll)A[y][i]*B[x][i]%MOD);
for(int i=0;i<=m;++i) FWT(C[i],1<<m,1);
int ans=0;
for(int i=(1<<m)-1;~i;--i) ans=add(ans*1526ll%MOD, C[pc(i)][i]);
cout<<ans<<"\n";
cout.flush();
return 0;
}