1. 程式人生 > >2018.11.07【校內模擬】異或(數位DP)(數學期望)

2018.11.07【校內模擬】異或(數位DP)(數學期望)

傳送門


解析:

蒟蒻考場上只想了隨機情況下的期望,於是就拿了部分分滾粗了。。。
其實最優情況下的期望我好像還推錯了,最後學習了標解才會的。

我好菜啊。。。希望今年NOIP不要打醬油就行了。

思路:

首先隨機的情況其實非常好想。我們只需要考慮每個位出現 1 1 的概率就行了,其實就是統計每個位出現 1

1 的方案數就行了,這個隨便亂搞一下都行,我是用的 O ( log 2 n )
O(\log^2n)
的做法。

然後最優化其實只需要按照數位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; }