双倍经验
get
第一个用cpp打的 666
题目描述
给定一个区间[0,n), 现在在这个区间内等概率的选取一个数x,然后有p的几率我们可以直接选择这个区间内异或上x和最大的数y,另外1-p的几率也是在这个区间中等概率的选择一个数y,求x⊕y的期望值.
形态形成场好像不是什么好东西 bl
n<=1018
我们将他分为成功与不成功两个部分计算
由题意知
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,可以边加边除防止爆掉
第二部分就是求
∑x⊕f(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;
}