【题解】[UOJ 310] UNR#2 黎明前的巧克力【FWT】
题意
将全集 \(S\) 分为 \(S_1,S_2,S_3\),要求 \(S_1,S_2\) 异或和相等且不全为空。求方案数。\(|S|,x\leq 10^6,\forall a\in S\)
题解
问题可转化为 \(T\subseteq S,T\neq \varnothing\),\(T\) 异或和为 \(0\),\(T\) 中每个元素贡献 \(2\) 的系数。
\(a_i\in S\) 可以写作 \(F_i(x)=x^{\varnothing}+2x^{a_i}\) 的集合幂级数,暴力异或卷积显然复杂度爆炸。
\(F_i(x)\) FWT 后每个位置为 \(-1\) 或 \(3\),我们只用统计每个位置乘了多少 \(3\) 和 \(-1\)。将所有幂级数加起来 FWT,等于 FWT 后加起来,于是解一个二元一次方程组,便可以知道每个位置有多少 \(3\) 和 \(-1\),以此求出 FWT 后相乘的结果,IFWT 即可。
#include<bits/stdc++.h>
using namespace std;
const int N=21,mod=998244353,inv4=(mod*3ll+1)/4;
int a[1<<N],n;
int b[1<<N];
int c[1<<N];
int qpow(int x,int y){
int ans=1;
while(y){
if(y&1)ans=ans*1ll*x%mod;
x=x*1ll*x%mod;
y>>=1;
}
return ans;
}
void fwt(int *a,int m,int tp){
for(int i=1;i<1<<m;i<<=1){
for(int j=0;j<1<<m;j+=i<<1){
for(int k=0;k<i;k++){
int x=a[j+k],y=a[i+j+k];
// cerr<<". "<<j+k<<" "<<i+j+k<<endl;
a[j+k]=x+y;a[i+j+k]=x-y;
a[j+k]>=mod&&(a[j+k]-=mod);
a[i+j+k]<0&&(a[i+j+k]+=mod);
}
}
}
if(tp==-1){
int q=qpow(1<<m,mod-2);
for(int i=0;i<1<<m;i++)
a[i]=a[i]*1ll*q%mod;
}
}
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++)scanf("%d",a+i);
for(int i=0;i<n;i++)b[0]++,b[a[i]]+=2;//随意,只要后面解方程改改即可
int m=0;
for(int i=0;i<n;i++)while(1<<m<=a[i])m++;
// n=1<<m;
fwt(b,m,1);
for(int i=0;i<1<<m;i++){
int cnt3=(n+b[i])*1ll*inv4%mod,cnt1=n-cnt3;
// cerr<<"> "<<b[i]<<" "<<cnt3<<" "<<cnt1<<endl;
c[i]=qpow(3,cnt3)*1ll*(cnt1%2?mod-1:1)%mod;
}
fwt(c,m,-1);
cout<<(c[0]-1+mod)%mod<<endl;
}