一、题目
二、解法
注意到答案是 2 63 2^{63} 263次方,可以分 k k k来讨论:
k = 1 k=1 k=1,求出所有值或起来的值,每一位有 1 2 \frac{1}{2} 21的概率有贡献,所以把这个值除以 2 2 2即可。
k = 2 k=2 k=2,枚举 i , j i,j i,j,考虑贡献是 2 i + j × p 2^{i+j}\times p 2i+j×p, p p p是 i , j i,j i,j同时有值的概率,考虑 a a a中0有没有 10 10 10或 01 01 01(是 i , j i,j i,j位上有没有值),如果有的话 p p p是 0.25 0.25 0.25,否则 p p p是 0.5 0.5 0.5,那么 0.25 0.25 0.25是怎么得出来的呢?分成三种情况,只有 01 01 01,只有 10 10 10,有 01 01 01和 10 10 10,计算就交给读者了,限于篇幅不给出详细说明。
k ≥ 3 k\geq3 k≥3,我们建出线性基,然后直接爆搜即可(想一想,每种情况的概率是 2 − c n t 2^{-cnt} 2−cnt, c n t cnt cnt是线性基里的个数,因为每种情况的个数是 2 n − c n t 2^{n-cnt} 2n−cnt),只用搜O( 2 63 / 3 2^{63/3} 263/3)那么次即可。
详细实现中还有一些细节,可以康康我的代码和注释。
#include <cstdio>
#define int unsigned long long
const int M = 100005;
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,m,k,sum,tot=1,a[M],p[70],ans,ret;
void fuck1()
{
for(int i=1;i<=n;i++)
ans|=a[i];
printf("%lld",(long long)(ans/2));
(ans%2)?puts(".5"):puts("");
}
void fuck2()
{
for(int i=1;i<=n;i++)
sum|=a[i];
for(int i=0;i<32;i++)
for(int j=0;j<32;j++)
if((sum>>i&1) && (sum>>j&1))
{
int f=0;
for(int k=1;k<=n;k++)
if((a[k]>>i&1)^(a[k]>>j&1)) {f=1;break;}
if(i+j<1+f) ret++;//保证不能有负数哟
else ans+=(1ll<<(i+j-1-f));
}
printf("%lld",(long long)(ans+ret/2));
(ret%2)?puts(".5"):puts("");
}
void ins(int x)
{
for(int i=m;~i;i--)
//无符号减到-1会炸
{
if(!(x>>i&1))continue;
if(!p[i]) {tot<<=1;p[i]=x;break;}
x^=p[i];
}
}
void dfs(int x,int y)
{
if(x>m)
{
int r=1;
for(int i=1;i<k;i++) r=r*y;
ans+=r/tot*y;r%=tot;r*=y;ret+=r;
ans+=ret/tot;ret%=tot;
//要这样来卡精度
return ;
}
if(p[x]) dfs(x+1,y),dfs(x+1,y^p[x]);
else dfs(x+1,y);
}
void fuck3()
{
if(k==3) m=21;
if(k==4) m=16;
if(k==5) m=13;
for(int i=1;i<=n;i++)
ins(a[i]);
dfs(0,0);
printf("%lld",(long long)ans);
ret?puts(".5"):puts("");
}
signed main()
{
n=read();k=read();
for(int i=1;i<=n;i++)
a[i]=read();
if(k==1) fuck1();
if(k==2) fuck2();
if(k>=3) fuck3();
}