CF623D Birthday

思路

男孩在游戏结束之前不能得到任何信息,而且每一次男孩抓到的人概
率是定值,所以游戏是否结束的概率只和每个人被猜了多少次有关。
假设第i个人男孩猜了(c_i) 遍,那么游戏结束的概率就是

(prod^{n}_{i=1}1-(1-p_i)^{c_i})

考虑在什么情况下男孩开始有成功的概率,显然是每个人都猜过一遍之后,这时候才能开始计算答案。

设恰好在第(i)局游戏结束的概率为(P_i),那么第(i)局结束贡献的期望就是(i*P_i),为了使期望局数最小,所以应该在(i)较小的时候,让(P_i)尽量大,这样才能让游戏尽可能早的结束。

所以男孩的最优策略一定是先把每个人猜一遍,然后每次选择一
个让游戏结束概率最大的猜测。

那么如何选择一个最优的猜测呢,因为期望是连乘得到的,所以我们一定会选择一个人多选择一次会游戏结束的概率变化尽量大的,即,(frac{1-(1-p^{c_{i}+1})}{1-(i-p^{c_i})})尽量大,这个东西我们可以用堆来处理一下,每次贪心选最大的,模拟(3e5)次,精度就不会出问题了。

需要注意的是 (prod^{n}_{i=1}1-(1-p_i)^{c_i})这个式子求出来的实在第(i)局游戏结束的概率,包括了在小于(i)局游戏结束的概率,假设 (g_i=prod^{n}_{i=1}1-(1-p_i)^{c_i}),那么恰好在第(i)局结束的概率(P_i)就是(g_i-g_{i-1}),把从(n-3e5)局之间的贡献计算求和就可以了。

code



#include<bits/stdc++.h>
#define re register
#define db double
#define gc getchar()
#define getch while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=gc;}
#define getnu while(ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+ch-'0';ch=gc;}
using namespace std;
inline int read(){int s=0,f=1;char ch=gc;getch;getnu;return s*f;}
const int maxn=110;
priority_queue<pair<db,int> >q;
db p[maxn];
db lans[maxn];
int main(){
	int n=read();
	db now=1;
	for(int i=1;i<=n;i++){
		p[i]=(db)read()/100;
		db nowans=1-(1-p[i]);
		db nxtans=1-(1-p[i])*(1-p[i]);
		q.push(make_pair(nxtans/nowans,i));
		lans[i]=nowans;
		now*=lans[i];
	}
	db ans=0;
	db lgl=now;
	ans+=now*n;
	for(int i=1;i<=300000;i++){
		db del=q.top().first;
		int id=q.top().second;
		q.pop();
		now/=lans[id];
		now*=(lans[id]*del);
		ans+=(now-lgl)*(n+i);
		lgl=now;
		lans[id]=lans[id]*del;
		db nxtans=(1-(1-lans[id])*(1-p[id]));
		q.push(make_pair(nxtans/lans[id],id));
	}
	printf("%.10lf
",ans);
}

原文地址:https://www.cnblogs.com/soda-ma/p/14026493.html