k=1的话非常好做,每个有1的位都有一半可能性提供贡献。由组合数的一些性质非常容易证明。
k=2的话,平方的式子展开可以发现要计算的是每一对位提供的贡献,于是需要计算每一对位被同时选中的概率。找出所有存在的相互绑定的位,这些位被同时选择的概率为0.5,而不被绑定的则为0.25。
对于k>=3,其实用与k=1,2相同的方法大力讨论也可以做。考虑更优美的做法。有一个性质:集合内数相互异或不影响答案。证明比(bing)较(bu)显(hui)然(xie)。于是构造出线性基。可以发现线性基里的元素很少,暴搜一发即可。
卡精度,不会证地有答案一定是整数或.5,全程整数各种乱搞即可。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; #define N 100010 #define ll unsigned long long ll read() { ll x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } int n,m,cnt=0; ll a[N],base[64],b[64]; bool flag[64],f[64][64]; ll ans,tot; void getbase() { for (int i=1;i<=n;i++) { ll x=a[i]; for (int j=63;~j;j--) if (x&(1ll<<j)) if (base[j]) x^=base[j]; else {base[j]=x;break;} } for (int j=63;~j;j--) if (base[j]) b[++cnt]=base[j]; } void solve1() { for (int i=1;i<64;i++) if (flag[i]) ans+=1ll<<i-1; cout<<ans;if (flag[0]) cout<<".5"; } void solve2() { for (int i=0;i<34;i++) for (int j=0;j<34;j++) f[i][j]=1; for (int i=1;i<=n;i++) for (int j=0;j<34;j++) for (int k=j+1;k<34;k++) if (((a[i]&(1ll<<j))>0)^((a[i]&(1ll<<k))>0)) f[j][k]=f[k][j]=0; for (int i=0;i<34;i++) if (flag[i]) for (int j=0;j<34;j++) if (flag[j]) { if (!f[i][j]) ans+=(1ll<<i+j); else ans+=(2ll<<i+j); tot+=ans/4,ans%=4; } cout<<tot;if (ans) cout<<".5"; } void solve3(int k,ll s) { if (k>cnt) { ll t=s*s*s;t/=(1<<cnt);for (int i=4;i<=m;i++) t*=s;tot+=t; t=s*s*s;t%=(1<<cnt);for (int i=4;i<=m;i++) t*=s; ans+=t;tot+=ans/(1<<cnt);ans%=(1<<cnt); } else solve3(k+1,s),solve3(k+1,s^b[k]); } int main() { #ifndef ONLINE_JUDGE freopen("bzoj3811.in","r",stdin); freopen("bzoj3811.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif n=read(),m=read(); for (int i=1;i<=n;i++) { a[i]=read(); for (int j=0;j<64;j++) if (a[i]&(1ll<<j)) flag[j]=1; } if (m==1) solve1(); if (m==2) solve2(); if (m>=3) { getbase(),solve3(1,0); cout<<tot;if (ans) cout<<".5"; } return 0; }