2018.11.07【校内模拟】异或(数位DP)(数学期望)

版权声明:转载请声明出处,谢谢配合。 https://blog.csdn.net/zxyoi_dreamer/article/details/83830067

传送门


解析:

蒟蒻考场上只想了随机情况下的期望,于是就拿了部分分滚粗了。。。
其实最优情况下的期望我好像还推错了,最后学习了标解才会的。

我好菜啊。。。希望今年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;
}

猜你喜欢

转载自blog.csdn.net/zxyoi_dreamer/article/details/83830067
今日推荐