bzoj3811: 玛里苟斯【线性基】

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;   
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值