1. 程式人生 > >[聯合集訓6-22] 疑惑 位運算+FFT

[聯合集訓6-22] 疑惑 位運算+FFT

根據期望的線性性,我們可以對每一位分別考慮其為1的概率。那麼假設一位有c00c11,選k個xor和為1的方案數顯然為i=0c1[i|2](c1i)(c0ki)。FFT即可。
程式碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 131100
#define ll long long
#define up(x,y) (x=(x+(y))%mod)
using namespace std;
const int mod=998244353
; int n,m,a[N],r[N],bit; ll fac[N],ifac[N],p[N],q[N],ans[N]; int read() { int x=0,f=1;char ch=getchar(); for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=-1; for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; return x*f; } ll ksm(ll a,ll b) { ll r=1; for(b=(b+mod-1
)%(mod-1);b;b>>=1,a=a*a%mod) if(b&1) r=r*a%mod; return r; } ll C(int a,int b) { return a<b?0:fac[a]*ifac[b]%mod*ifac[a-b]%mod; } void ntt(ll a[],int n,int dft) { for(int i=0;i<n;i++) if(i<r[i]) swap(a[i],a[r[i]]); for(int i=1;i<n;i<<=1) { ll wn=ksm(3
,(mod-1)/(i<<1)*dft); for(int j=0;j<n;j+=(i<<1)) { ll wk=1; for(int k=j;k<j+i;k++) { ll x=a[k],y=wk*a[k+i]%mod; a[k]=(x+y)%mod;a[k+i]=(x-y+mod)%mod; wk=wk*wn%mod; } } } if(dft==-1) for(int i=0,inv=ksm(n,mod-2);i<n;i++) a[i]=a[i]*inv%mod; } int main() { n=read(); for(int i=1;i<=n;i++) { a[i]=read(); while(a[i]>=(1<<bit)) bit++; } fac[0]=1; for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod; ifac[n]=ksm(fac[n],mod-2); for(int i=n-1;i>=0;i--) ifac[i]=ifac[i+1]*(i+1)%mod; for(m=1;n>=m;m<<=1); for(int i=0;i<m;i++) r[i]=(r[i>>1]>>1)|((i&1)*(m>>1)); for(int k=0;k<bit;k++) { int cnt[2];cnt[0]=cnt[1]=0; for(int i=1;i<=n;i++) cnt[(a[i]>>k)&1]++; memset(p,0,sizeof(p)); memset(q,0,sizeof(q)); for(int i=0;i<=n;i++) { if(i&1) p[i]=C(cnt[1],i); q[i]=C(cnt[0],i); } ntt(p,m,1);ntt(q,m,1); for(int i=0;i<m;i++) p[i]=p[i]*q[i]%mod; ntt(p,m,-1); for(int i=0;i<=n;i++) up(ans[i],p[i]*(1ll<<k)); } for(int i=0;i<=n;i++) ans[i]=ans[i]*ksm(C(n,i),mod-2)%mod; for(int i=n;i;i--) printf("%lld ",ans[i]); return 0; }