#FWT#ABC367 G - Sum of (XOR^K or 0)
题目
给定一个长度为 \(n\) 的序列 \(a\),求所有长度为 \(m\) 的倍数的子序列异或和的 \(k\) 次方之和。
分析
首先异或和以及 \(k\) 次方进行分离,应该先求出每种异或和对应的方案数,再赋以 \(k\) 次方。
抽象一点也就是 \(\sum_{i=0}^{2^o-1}[x^iy^0]i^k\prod_{j=1}^n(1+x^{a_j}y)\),其中 \(x\) 是异或卷积,\(y\) 是针对 \(m\) 的循环卷积。
如果直接 \(n\) 个多项式相乘显然不行,观察对于 \(1(x^0)\) 经过 FWT 后为全 \(1\) 的序列,而 \(x^{a_j}\) 经过 FWT 后为 \(\pm 1\) 的序列,
那么进行 FWT 后,后面的式子实际是 \([y^0](1+y)^c(1-y)^{n-c}\),也就转化成求有多少个 \(a_j\) 经过 FWT 变换能在 \(i\) 的位置产生 \(1\)。
由于 FWT 的线性性,将 \(a_i\) 对应位置加一得到的 \(f\) 数组进行 FWT 后,\(c-(n-c)=f_i\),那么就能把 \(c\) 解出来。
对于 \(y\) 的卷积可以在 \(O(nm)\) 的时间预处理,那么代入该式子后进行逆 FWT 即可。
代码
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
using namespace std;
const int mod=998244353,inv=499122177,N=1048576;
int f[N],n,m,k,cnt,mx,ans,mi[N],prime[N],dp[N],g0[N][111],g1[N][111],Cnt;
int iut(){
int ans=0; char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=ans*10+c-48,c=getchar();
return ans;
}
void print(int ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
int mo1(int x,int y){return x+y>=mod?x+y-mod:x+y;}
int mo2(int x,int y){return x<y?x-y+mod:x-y;}
void FWT_XOR(int *f,int opt){
for (int p=2;p<=n;p<<=1){
int len=p>>1;
for (int i=0;i<n;i+=p)
for (int j=i;j<i+len;++j){
int t1=f[j],t2=f[j+len];
f[j]=mo1(t1,t2),f[j+len]=mo2(t1,t2);
if (opt==-1) f[j]=1ll*f[j]*inv%mod,
f[j+len]=1ll*f[j+len]*inv%mod;
}
}
}
int ksm(int x,int y){
int ans=1;
for (;y;y>>=1,x=1ll*x*x%mod)
if (y&1) ans=1ll*ans*x%mod;
return ans;
}
int main(){
cnt=iut(),m=iut(),k=iut();
for (int i=0;i<cnt;++i){
int x=iut();
mx=max(mx,x);
++f[x];
}
for (n=1;n<=mx;n<<=1);
mi[1]=1;
for (int i=2;i<=n-1;++i){
if (!mi[i]) mi[i]=ksm(i,k),prime[++Cnt]=i;
for (int j=1;j<=Cnt&&prime[j]<=(n-1)/i;++j){
mi[i*prime[j]]=1ll*mi[i]*mi[prime[j]]%mod;
if (i%prime[j]==0) break;
}
}
FWT_XOR(f,1);
g0[0][0]=g1[0][0]=1;
for (int i=1;i<=cnt;++i){
for (int j=0;j<m;++j){
g0[i][j==m-1?0:(j+1)]=mo1(g0[i-1][j==m-1?0:(j+1)],g0[i-1][j]);
g1[i][j==m-1?0:(j+1)]=mo2(g1[i-1][j==m-1?0:(j+1)],g1[i-1][j]);
}
}
for (int i=0;i<=cnt;++i){
dp[i]=1ll*g0[i][0]*g1[cnt-i][0]%mod;
for (int j=1;j<m;++j)
dp[i]=mo1(dp[i],1ll*g0[i][j]*g1[cnt-i][m-j]%mod);
}
for (int i=0;i<n;++i) f[i]=dp[((cnt+f[i])%mod)>>1];
FWT_XOR(f,-1);
for (int i=1;i<n;++i) ans=mo1(ans,1ll*mi[i]*f[i]%mod);
return !printf("%d",ans);
}