uoj310. 【UNR #2】黎明前的巧克力
题目描述:
题解:
WTF。
看题解看了一个小时才看明白。
首先有状态$f[i][j]$表示前$i$个东西两人取,最后两人异或和为$j$的有多少方案。
转移为$f[i][j]=f[i-1][j]+2*f[i-1][j \oplus a[i]]$。
显然跑FWT做异或卷积(显然会T)。
发现卷积中每次卷的是{1,0,0,……,0,2,0……}这样一个东西。
打表发现FWT后每一项是-1或3。
其实很好解释,从贡献的角度讲,0位的贡献都是1,而$a[i]$位的贡献是2或-2,所以是3或-1。
考虑将所有的$a[i]$放在一起做FWT。
这样的话每组对每一位上的贡献是-1或3,共有$n$组,贡献和为$s$。
设贡献为$3$的有$x$组,那么$3x-(n-x)=s$,有$x=\frac{n+s}{4}$。
然后快速幂再卷回去就好了。
代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; const int N = 2000050; const int MOD = 998244353; const int inv_2 = (MOD+1)/2; const int inv_4 = 1ll*inv_2*inv_2%MOD; template<typename T> inline void read(T&x) { T f = 1,c = 0;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){c=c*10+ch-'0';ch=getchar();} x = f*c; } template<typename T>inline void Mod(T&x){if(x>=MOD)x-=MOD;} ll fastpow(ll x,int y) { ll ret = 1; while(y) { if(y&1)ret=ret*x%MOD; x=x*x%MOD;y>>=1; } return ret; } int n,a[N],lim; void fwt(int*a,int len,int k) { for(int i=1;i<len;i<<=1) for(int j=0;j<len;j+=(i<<1)) for(int o=0;o<i;o++) { int w1 = a[j+o],w2 = a[j+o+i]; Mod(a[j+o] = w1+w2),Mod(a[j+o+i]=w1+MOD-w2); if(k==-1)a[j+o]=1ll*a[j+o]*inv_2%MOD,a[j+o+i]=1ll*a[j+o+i]*inv_2%MOD; } } int main() { read(n);int mx=0; for(int i=1,x;i<=n;i++) { read(x); if(x>mx)mx=x; a[0]++,a[x]+=2; } lim = 1; while(lim<=mx)lim<<=1; fwt(a,lim,1); for(int i=0;i<lim;i++) { int now = 1ll*(n+a[i])*inv_4%MOD; a[i] = fastpow(3,now); if((n-now)&1)a[i]=MOD-a[i]; } fwt(a,lim,-1); printf("%d\n",(a[0]+MOD-1)%MOD); return 0; }