BZOJ3811 玛里苟斯(线性基+概率期望)

  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;
}

 

转载于:https://www.cnblogs.com/Gloid/p/9741470.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值