【分析】
考虑 ((ivee j)=(iwedge j)vee (ioplus j))
记 (x=ivee j,y=ioplus jRightarrow x=yvee kRightarrow k=xoplus y(k,ysubseteq x))
(quad C_k)
(displaystyle =sum_{iwedge j=k}A_{ioplus j}B_{ivee j})
(displaystyle =sum_{xoplus y=k}[ysubseteq x]A_yB_xsum_isum_j[ivee j=x][i wedge j=k])
(displaystyle =sum_{xoplus y=k}[ywedge x=y]A_yB_x2^{pc(x)-pc(k)})
其中 (pc(n)) 代表 (n) 二进制中 (1) 的个数
考虑到 (k,y) 均为 (x) 的子集,且 (k=xoplus y)
故 (pc(x)-pc(k)=pc(y)) ,代入原式得到
(displaystyle C_k=sum_{xoplus y=k\ ywedge x=y}(A_ycdot 2^{pc(y)})cdot B_x)
考虑到式子的本质,像是对于 (k) ,枚举另一个与它不交的集合 (y) ,把两者的并集和 (y) 集合的贡献统计到 (k) 上
类似的东西是普通的子集卷积: (displaystyle C_n=sum_{ivee j=n\ iwedge 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(nlog^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<<"
";
cout.flush();
return 0;
}