BZOJ 3811 玛里苟斯(线性基)

Description

魔法之龙玛里苟斯最近在为加基森拍卖师的削弱而感到伤心,于是他想了一道数学题。

S 是一个可重集合,S={a1,a2,,an}

等概率随机取 S 的一个子集A={ai1,,aim}

计算出 A 中所有元素异或 x, 求 xk 的期望。

Input

第一行两个正整数 n,k

以下 n 行每行一个整数,表示ai

1n1000001k5ai0 。最终答案小于 263 k=1,2,3,4,5 各自占用 20% 的数据

Output

如果结果是整数,直接输出。如果结果是小数(显然这个小数是有限的),输出精确值(末尾不加多余的0)。

Sample Input

4 2

0

1

2

3

Sample Output

3.5

Solution

k=1 时,考虑每一位对答案的影响,对于第 i 位,如果这n个数中存在一些数在第 i 位是1,由于从一些数中选出偶数个数和选出奇数个数概率相等,故有 0.5 的概率在第 i 位是1,否则对答案的贡献为 0 ,故只要把这n个数或起来除 2 即可

k=2时,考虑异或和二进制下平方的表示,设该异或和二进制表示为 b1b2...bm ,那么对答案的贡献为 i,jbibj2i+j ,考虑 bi=bj=1 的概率,只看这 n 个数的第i位和第 j 位,(0,0)不用考虑,只用考虑 (1,0),(0,1),(1,1) 这三种情况,设这三种分别拿 a,b,c 个,那么为了得到 (1,1) 需满足 a+c,b+c 均为奇数,若 c 为奇数,那么a,b都是偶数,若 c 为偶数,那么a,b都是奇数,设这 n 个数中这两位是(1,0),(0,1),(1,1)这三种情况的分别由 A,B,C 个,那么如果 A=B=0 ,那么要得到 (1,1) 只能拿奇数个 (1,1) ,概率是 0.5 ;如果 A>0,B=0(A=0,B>0 同理),拿奇数个 (1,1) 和偶数个 (1,0) 概率是 0.25 ; 如果 A,B>0 ,则拿奇数个 (1,1) 和偶数个 (1,0) 和偶数个 (0,1) 的概率是 0.125 ,拿奇数个 (1,1) 和偶数个 (1,0) 和偶数个 (0,1) 的概率是 0.125 ,加起来就是 0.25 ,故只要存在 (1,0) (0,1) 概率就是 0.25 ,否则概率是 0.5 ,故只需枚举 i,j 考虑着两位对答案的贡献即可

k3 时,由于答案不超过 263 ,故线性基维数 m 不会超过21个,直接求出线性基枚举子集即可求和再除以 2m 即可, 但是运算过程中答案可能会超过 263 ,可以把数 a 表示成a=a2m2m+a%2m,拿除数和余数去运算

Code

#include<cstdio>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn=100005;
int n,k,base[30];
ll a[maxn];
int main()
{
    scanf("%d%d",&n,&k);
    if(k==1)
    {
        ull ans=0,temp;
        for(int i=1;i<=n;i++)
        {
            scanf("%llu",&temp);
            ans|=temp;
        }
        printf("%llu%s\n",ans>>1,ans&1?".5\n":"\n");
    }
    else if(k==2)
    {
        ll s=0;
        for(int i=1;i<=n;i++)scanf("%lld",&a[i]),s|=a[i];
        ll ans=0;
        int res=0;
        for(int i=0;i<32;i++)
            for(int j=0;j<32;j++)
            {
                if(!(s>>i&1)||!(s>>j&1))continue;
                int f=0;
                //f表示是否存在(0,1)或者(1,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<0)res++;
                else ans+=(1ll<<(i+j-1-f)); 
            }
        ans+=res>>1;
        printf("%lld%s",ans,res&1?".5\n":"\n");
    }
    else
    {
        for(int i=1;i<=n;i++)
        {
            int temp;
            scanf("%d",&temp);
            for(int j=22;j>=0;j--)
                if(temp>>j&1)
                {
                    if(!base[j])
                    {
                        base[j]=temp;
                        break;
                    }
                    else temp^=base[j];
                }
        }
        int m=0;
        for(int i=0;i<=22;i++)
            if(base[i])base[m++]=base[i];
        int M=1<<m;
        ll ans=0,res=0;
        for(int i=1;i<M;i++)
        {
            int temp=0;
            for(int j=0;j<m;j++)
                if((i>>j)&1)temp^=base[j];
            ll x=0,y=1;
            for(int j=0;j<k;j++)
            {
                x*=temp,y*=temp;
                x+=y>>m,y&=(M-1);
            }
            ans+=x,res+=y;
            ans+=res>>m,res&=(M-1);
        }
        if(res)printf("%lld.5\n",ans);
        else printf("%lld\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值