【NOIP2013模拟】期望异或&&【NOI2015模拟9.7】大新闻

Description

在区间[0,n)中随机生成一个整数x,然后,有p的概率选择[0,n)中与x异或值最大的y,否则在区间[0,n)中随机选择一个整数y。求 xy 的期望值。
n<=10^18

Solution

首先把答案写出来:

Ans=1pn2i=0n1j=0n1ij+pni=0n1if(i)

其中 f(i) 表示[0,n)中与i异或值最大的那个数。
然后,我们把答案分成两部分来求。

Part 1

也就是相当于求

i=0n1j=0n1ij

很显然可以用二进制来一位一位判断。
因为每一位互不影响,期望值相加即可。
一般的,区间[ 0 ,2i)第i位为0,区间[ 2i , 2i+1 )为1。
然后,区间[ k2i+1 , k2i+1+2i )第i位为0,区间[ k2i+1+2i , (k+1)2i+1 )第i位为1。
所以,区间[0,n)中二进制第i位为1的个数是:
n2i+12i+max(nmod2i+12i,0)

设为pi,那么这一位对答案的贡献为 2(1p)pi(npi)2i
因为最后要除以n^2,可以先除掉,避免答案过大而爆炸。
于是把上式/n,那么对答案的贡献就是 2(1p)pi(1pi)2i

Part 2

也就是相当于求

i=0n1if(i)

这个东西本蒟蒻暂时没有想到什么高深算法,只能上数位dp了。
设F[i,j,k],表示现在做到第i位(从高到低),x是不是达到上界,f(x)是不是达到上界的和,G[i,j,k]表示这样的数的个数。
Dp方程还是很好想的,只不过有两个数需要限制而已。。不过限制都是一样的。

Code

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef double db;
typedef long long ll;
db p,ans1,ans2,f[61][2][2];
ll n,mi[61],g[61][2][2];
int a[61],tot;
int main() {
    scanf("%lld%lf",&n,&p);int l=log2(n);tot=-1;
    if (n==1) {printf("0.000000");return 0;}
    for(ll x=n-1;x;x/=2) a[++tot]=(x&1);
    mi[0]=1;fo(i,1,l+1) mi[i]=mi[i-1]*2;
    fo(i,0,l) {
        db f=(n/mi[i+1]*mi[i]+
        max((ll)0,n%mi[i+1]-mi[i]))/(db)n;
        ans1+=2*f*(1-f)*mi[i]*(1-p);
    }
    f[tot][0][1]=f[tot][1][0]=mi[tot];
    g[tot][0][1]=g[tot][1][0]=1;
    fd(i,tot,1) 
        fo(j,0,1)
            fo(k,0,1) 
                if (g[i][j][k]) 
                    fo(x,0,1) {
                        if (!j&&x>a[i-1]) continue;
                        int y=0,z=0;
                        if (j||x<a[i-1]) y=1;
                        if (k||1-x<a[i-1]) z=1;
                        f[i-1][y][z]+=f[i][j][k]+(k||1-x<=a[i-1])*
                        mi[i-1]*g[i][j][k];
                        g[i-1][y][z]+=g[i][j][k];
                    }
    fo(i,0,1) fo(j,0,1) ans2+=f[0][i][j]*p;
    ans2/=(db)n;
    printf("%.6lf",ans1+ans2);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值