uoj310【UNR #2】黎明前的巧克力(FWT)
uoj310【UNR #2】黎明前的巧克力(FWT)
题解时间
对非零项极少的FWT的优化。
首先有个十分好想的DP: $ f[i][j] $ 表示考虑了前 $ i $ 个且异或和为 $ j $ 的方案数,
有 $ f[i][j]= f[i-1][j] + 2 * f[i-1][j \oplus a[i]] $ 。
可以考虑FWT,但很明显时间复杂度没有优化。
但另一方面,每层的卷积卷的都是 $ 1,0,0,...,2,0,0,... $ 的形式,
这样一来卷之后每项都是 $ -1 $ 或 $ 3 $ 。
因此大胆一点直接把所有的式子加到一起卷积,
卷完有什么用呢?
这样卷出来的结果是每层卷积相加,而正常FWT求解是每层卷积相乘。
每一位都是共计 $ n $ 个 $ -1 $ 和 $ 3 $ 加一起,所以可以求出每一位上两种数的个数。
由于知道了每一位上两种数的个数,直接每一位快速幂就能求出原来的相乘结果。
#include<bits/stdc++.h>
using namespace std;
typedef long long lint;
struct pat{int x,y;pat(int x=0,int y=0):x(x),y(y){}bool operator<(const pat &p)const{return x==p.x?y<p.y:x<p.x;}};
template<typename TP>inline void read(TP &tar)
{
TP ret=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){ret=ret*10+(ch-'0');ch=getchar();}
tar=ret*f;
}
template<typename TP,typename... Args>inline void read(TP& t,Args&... args){read(t),read(args...);}
namespace RKK
{
const int N=2000011;
const int defaultlen=1048576;
const int mo=998244353,inv2=499122177,inv4=748683265;
void doadd(int &a,int b){if((a+=b)>=mo) a-=mo;}int add(int a,int b){return (a+=b)>=mo?a-mo:a;}
void domul(int &a,int b){a=1ll*a*b%mo;}int mul(int a,int b){return 1ll*a*b%mo;}
int fpow(int a,int p){int ret=1;while(p){if(p&1) domul(ret,a);domul(a,a),p>>=1;}return ret;}
int n,a[N];
void fwtxor(int *a,int len,int tp)
{
for(int i=1,w1,w2;i<len;i<<=1)
for(int j=0;j<len;j+=i<<1)
for(int k=0;k<i;k++)
{
w1=a[j+k],w2=a[j+k+i];
a[j+k]=add(w1,w2),a[j+k+i]=add(w1,mo-w2);
if(tp==-1) domul(a[j+k],inv2),domul(a[j+k+i],inv2);
}
}
int main()
{
read(n);for(int i=1,w;i<=n;i++) read(w),a[0]+=1,a[w]+=2;
fwtxor(a,defaultlen,1);
for(int i=0,cnt1,cnt3;i<defaultlen;i++)
{
cnt3=mul(add(n,a[i]),inv4),cnt1=n-cnt3;
a[i]=(cnt1&1)?mo-fpow(3,cnt3):fpow(3,cnt3);
}
fwtxor(a,defaultlen,-1);
printf("%d\n",add(a[0],mo-1));
return 0;
}
}
int main(){return RKK::main();}