传送门
解析:
蒟蒻考场上只想了随机情况下的期望,于是就拿了部分分滚粗了。。。
其实最优情况下的期望我好像还推错了,最后学习了标解才会的。
我好菜啊。。。希望今年NOIP不要打酱油就行了。
思路:
首先随机的情况其实非常好想。我们只需要考虑每个位出现 1 1 1的概率就行了,其实就是统计每个位出现 1 1 1的方案数就行了,这个随便乱搞一下都行,我是用的 O ( log 2 n ) O(\log^2n) O(log2n)的做法。
然后最优化其实只需要按照数位DP的套路来就行了,我们固定一个前缀紧挨上界,然后考虑后面能够怎么填就行了。去看标解吧,写得够清楚了。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const
inline void out(double x){
re int cnt=0;
while(x>10)++cnt,x/=10;
while(x<1.0)--cnt,x*=10;
printf("%.5f %d",x,cnt);
}
inline double calc0(ll n){
static ll cnt[65];
memset(cnt,0,sizeof cnt);
for(int re i=62;~i;--i){
if(n&(1ll<<i)){
for(int re j=i+1;j<=62;++j)if(n&(1ll<<j))cnt[j]+=1ll<<i;
for(int re j=i-1;~j;--j)cnt[j]+=1ll<<(i-1);
}
}
double res=0;
for(int re i=62;~i;--i){
if(cnt[i]){
double x=(double)cnt[i]/(1.0*n);
res+=2*x*(1-x)*(1ll<<i);
}
}
return res;
}
inline double calc1(ll n){
ll delta,hi=1,tot;
for(--n;hi<=n;hi<<=1);
delta=hi-1;hi>>=1;
double res=1.0*delta*(n+1-hi)/(1.0*n+1);
res+=1.0*hi*hi/(1.0*n+1);
tot=hi,delta>>=1;
while(hi>1){
hi>>=1;delta>>=1;
if(n&hi){
res+=1.0*tot*hi/(1.0*n+1);
res+=1.0*(tot>>=1)*delta/(1.0*n+1);
}
else res+=1.0*(tot>>1)*hi/(1.0*n+1);
}
return res;
}
ll n;
double p;
signed main(){
cin>>n>>p;
out(calc0(n)*(1-p)+calc1(n)*p);
return 0;
}