Description
魔法之龙玛里苟斯最近在为加基森拍卖师的削弱而感到伤心,于是他想了一道数学题。
S 是一个可重集合,S={a1,a2,…,an}。
等概率随机取 S 的一个子集 A={ai1,…,aim}。
计算出 A 中所有元素异或 x, 求 x^k 的期望。
Input
第一行两个正整数 n, k。
以下 n 行每行一个整数,表示 ai。
Output
如果结果是整数,直接输出。如果结果是小数(显然这个小数是有限的),输出精确值(末尾不加多余的 0)。
Sample Input
4 2
0
1
2
3
Sample Output
3.5
HINT
限制与约定
1≤n≤100000,1≤k≤5,ai≥0。最终答案小于 2^63 。k=1,2,3,4,5 各自占用 20% 的数据
解题思路:
k=1比较简单,考虑把单独计算每一位。
如果有至少一个数这一位是1,那么很显然,这一位为0和为1的概率相等,都是0.5;否则这一位只可能是0。
k=2时考虑异或以后做竖式乘法,枚举二进制任意两位的平方,当这两位的平方有贡献当且仅当这两位都是1。
然后计算一下这两位都是1的概率,假设两位分别为i,j,那么这两位组合的期望就是2^{i+j}∗p。
p也比较好算,如果所有数在i和j两位都为1的话那么概率就是0.5,有一位都为0的话就是0,否则概率就是0.25。
k>=3时,因为题目说答案小于2^63,那么每个数肯定最多只有22位。
根据线性基的性质,n个数异或以后的数出现概率与线性基中这个数出现概率相同。
假设线性基元素有k个,那么n个数异或以后每个数出现概率就是1/2^k,于是我们直接写一个爆搜,搜出线性基的所有异或情况就行了,注意要边加边模,不能用double。
#include<bits/stdc++.h>
#define ll unsigned long long
using namespace std;
ll getint()
{
ll i=0,f=1;char c;
for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
if(c=='-')f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
const int N=100005;
int n,k;
ll a[N],base[N],ans,ret,sz,tot,cnt;
void solve1()
{
ll res=0;
for(int i=1;i<=n;i++)res|=a[i];
printf("%lld",res>>1);
(res&1)?puts(".5"):putchar('\n');
}
void solve2()
{
ll res=0,sum=0,cnt=0;
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 flag=0;
for(int k=1;k<=n;k++)
if((a[k]>>i&1)^(a[k]>>j&1)){flag=1;break;}
i+j-1-flag<0?cnt++:res+=(1ll<<i+j-1-flag);
}
res+=cnt/2;printf("%lld",res);
(cnt&1)?puts(".5"):putchar('\n');
}
void dfs(int x,int cur)
{
if (x>sz){
ll res=1;
for (int i=1;i<k;++i) res*=cur;
if (res<tot)
{
res*=cur,ret+=res;
if (ret>=tot) ans+=ret/tot,ret%=tot;
}
else
{
ans+=res/tot*cur,res%=tot,res*=cur,ret+=res;
if (ret>=tot) ans+=ret/tot,ret%=tot;
}
return;
}
if(base[x])dfs(x+1,cur),dfs(x+1,cur^base[x]);
else dfs(x+1,cur);
}
void solve3()
{
sz=(k==3?21:(k==4?15:12)),cnt=0;
for(int i=1;i<=n;i++)
{
ll x=a[i];
for(int j=sz;j>=0;j--)if(x>>j&1)
{
if(!base[j]){base[j]=x,cnt++;break;}
else x^=base[j];
}
}
tot=1<<cnt,dfs(0,0);
printf("%lld",ans);ret?puts(".5"):putchar('\n');
}
int main()
{
//freopen("lx.in","r",stdin);
n=getint(),k=getint();
for(int i=1;i<=n;i++)a[i]=getint();
if(k==1)solve1();
else if(k==2)solve2();
else solve3();
return 0;
}