jzoj3442&3816 期望异或&&大新闻

双倍经验 get
第一个用cpp打的 666

题目描述

给定一个区间[0,n), 现在在这个区间内等概率的选取一个数x,然后有p的几率我们可以直接选择这个区间内异或上x和最大的数y,另外1-p的几率也是在这个区间中等概率的选择一个数y,求x⊕y的期望值.
形态形成场好像不是什么好东西 bl
n<=1018

我们将他分为成功与不成功两个部分计算
由题意知

Ans=(1p)1n2xy+p1nxf(x)

f(x)=在此区间内与x异或 和最大的数

因为是异或,显然每一位是可以独立算贡献的 (论位运算的拆位重要性)
那么问题就转化为了有a个0,b个1 共n个,等概率取x,y,求x⊕y=1的期望值
这个显然就是a/n*b/n*2,问题是a和b该如何算 我们假设求a

那问题就又变成了求该区间有多少个数该位=1
要考虑限制的当前位=1/0的情况,
当前是被[]括起来的位
11100[0]101
假设为0
依旧分成两部分来看pre, suf
首先对于前面的pre的选择(0~11100-1),我们可以知道每一个都有 23=111+1 (0~2^3-1)个suf使得当前位为0,然后假如pre选11100,那么就只有suf+1个使得当前位为0(0~suf)

假设为1
那所有pre都可以取 23 (0~2^3-1)个suf为0

        if (bit[i]==0) {
            ff=pre*mi[i-1]+suf+1; 
        } else { 
            ff=((pre+1)*mi[i-1]);
        }

再乘上1-p和概率这样就可以计算出ans1,可以边加边除防止爆掉

第二部分就是求 xf(x)
我们设 fi,j,k 表示第len~i位对答案的贡献,j,k分别表示x,f(x)下一次有没有限制
g[i,j,k]表示第i位的限制状态为j,k的数的个数.

枚举i,j,k,以及当前位选的x=0/1,我们知道有:
设现在要从i更新到i-1
当x有限制且x>a[i-1]显然这个不合法,舍去
我们判一下在现在的j,k的前提下,取x,!x是否会达到限制,也就是求新的j’,k’

然后再判,能否取!x做f(x)
如果k没限制,那么肯定能取!x,或者是没超过限制,那都可以取!x
如果能取那就有贡献,如果不能取那就没贡献

其实这里是有一种特殊情况的,!x>限制且k=1
但是看一下判断的条件
if (!k || bit[i-1]>!x) kk=0;
if (!k || bit[i-1]>=!x) toAns=mi[i-2]; else toAns=0;
然后能发现,如果是上述特殊情况的话,kk=1,这样其实就是无法取1接着取0的样子.

code

#include<cstdio>
#include<cstdlib>
#define fo(i,x,y) for(int i=x;i<=y;i++)
using namespace std;
long long n;
double p, ans1, ans2, f[61][2][2];
int bit[61];
long long mi[61], g[61][2][2];

int main() {
    freopen("3442.in","r",stdin);
    scanf("%lld %lf",&n,&p);
    if (n==0) {printf("0.000000"); return 0;}
    n--;

    mi[0]=1;
    for (int i=1; i<=60; i++) mi[i]=mi[i-1]*2;

    bit[0]=0;
    for (long long tmp=n; tmp>0; tmp/=2) bit[++bit[0]]=tmp%2;
    long long a=n*n;

    fo(i,1,bit[0]) {
        long long pre=n/(mi[i]), suf=n%(mi[i]);
        double ff = 0.0;
        if (bit[i]==0) {
            ff=pre*mi[i-1]+suf+1; 
        } else { 
            ff=((pre+1)*mi[i-1]);
        }
        double add=ff*(n+1-ff);
        add/=(double)(n+1);
        add/=(double)(n+1);
        add*=mi[i-1]*(1-p);
        ans1+=add;
    }

    ans1=ans1*2;

    //part 2
    f[bit[0]][0][1]=f[bit[0]][1][0]=mi[bit[0]-1];
    g[bit[0]][0][1]=g[bit[0]][1][0]=1;

    for (int i=bit[0]; i>0; i--)
        fo(j,0,1)
            fo(k,0,1)
                if (g[i][j][k]) {
                    fo(x,0,1) {
                        if (j && (x>bit[i-1])) continue;
                        long long jj=1,kk=1,toAns=0;  
                        if (!j || bit[i-1]>x) jj=0;
                        if (!k || bit[i-1]>!x) kk=0;
                        if (!k || bit[i-1]>=!x) toAns=mi[i-2]; else toAns=0;

                        f[i-1][jj][kk]+=f[i][j][k]+toAns*g[i][j][k];
                        g[i-1][jj][kk]+=g[i][j][k];
                    }
                }
    ans2=0.0;
    fo(j,0,1) fo(k,0,1) ans2+=f[1][j][k]*p/(double)(n+1);

    double ans3=ans1+ans2;
    int len=0;

    if (ans3>=10) {
        while (ans3>=10) {
            len++;
            ans3/=10;
        }
    } else 
    if (ans3<1 && ans3!=0){
        while (ans3<1) {
            ans3*=10;
            len--;
        }
    }

    //freopen("3442.out","w",stdout);
    printf("%.8lf %d",ans3,len);
    return 0; 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值