[UOJ UNR#2 黎明前的巧克力]
来自FallDream的博客,未经允许,请勿转载,谢谢。
很奇妙的一道题
首先不难发现一个暴力做法,就是f[i]表示异或和为i的答案数,每次FWT上一个F数组,其中F[0]=1,F[ai]=2,最后输出f[0]即可。
这样我就考虑从FWT之后的数组入手。
首先发现F[0]=1只会让最后的数组全部+1,所以只考虑F[ai]=2的影响。
发现每个项只会是3或者-1,这取决于FWT过程中的取反次数。
所以可以设计一个dp,f[i][x]表示分治到第i层,x是2的方案数,F[i][x]表示....,x是-2的方案数。
这样模拟FWT进行dp即可,最后通过快速幂计算出变换后最终的数组,再逆变换回去就是答案啦。
#include<iostream> #include<cstring> #include<cstdio> #define MN 1048576 #define mod 998244353 using namespace std; inline int read() { int x=0;char ch=getchar(); while(ch<'0'||ch>'9') ch=getchar(); while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar(); return x; } const int Inv2=(mod+1)/2; int s[MN+5],S[MN+5],n,f[22][MN+5],F[22][MN+5],num[MN+5],sum; inline int pow(int x,int k) { for(sum=1;k;k>>=1,x=1LL*x*x%mod) if(k&1) sum=1LL*sum*x%mod; return sum; } void FWT(int l,int r) { if(l==r) return; int mid=l+r>>1;FWT(l,mid);FWT(mid+1,r); for(int i=0;i<=mid-l;++i) { int x=s[l+i],y=s[mid+1+i]; s[l+i]=1LL*(x+y)*Inv2%mod; s[mid+1+i]=1LL*(x-y+mod)*Inv2%mod; } } void Solve(int l,int r,int dep) { if(l==r){f[dep][l]=num[l];return;} int mid=l+r>>1;Solve(l,mid,dep+1);Solve(mid+1,r,dep+1); for(int i=0;i<=mid-l;++i) { f[dep][l+i]=f[dep+1][l+i]+f[dep+1][mid+1+i]; F[dep][l+i]=F[dep+1][l+i]+F[dep+1][mid+1+i]; f[dep][mid+1+i]=f[dep+1][l+i]+F[dep+1][mid+1+i]; F[dep][mid+1+i]=F[dep+1][l+i]+f[dep+1][mid+1+i]; } } int main() { n=read(); for(int i=1;i<=n;++i) ++num[read()]; Solve(0,MN-1,1); for(int i=0;i<MN;++i) { s[i]=pow(3,f[1][i]); if(F[1][i]&1) s[i]=(mod-s[i])%mod; } FWT(0,MN-1); printf("%d\n",(s[0]-1+mod)%mod); return 0; }
FallDream代表秋之国向您问好!
欢迎您来我的博客www.cnblogs.com/FallDream