[LnOI2019]加特林轮盘赌(DP,概率期望)
题解:
首先特判掉(p=0/1)的情况...
先考虑如果(k=1)怎么做到(n^2)的时间复杂度
设(f[i])表示有(i)个人,(k=1)的时候幸存的概率
设(g[i][j])表示(i)个人每个人挨一下恰好死(j)个人的概率
我们就可以列出转移方程:
[f[i]=(1-p)sum_{j=1}^{i-1}{f[j]*g[i-j]}+f[i]*g[i][0]
]
- 含义:枚举打了一圈后剩下多少人,乘(1-p)是因为要保证自己不死
把含有(f[i])的项全部移到左边,化简得:
[f[i]=frac{(1-p)sum_{j=1}^{i-1}{f[j]*g[i-1][i-j]}}{1-g[i][0]}
]
于是我们就可以愉快地(dp)啦
数组(g)存不下来,(dp)的时候动态更新就好啦
如果(k!=1)怎么办呢?
我们可以把(1sim k-1)的这些人先打一遍,对于每种剩下的人数分别计算答案,然后按照概率加起来就好了
(double)运算常数很大,比赛时一直(TLE)在(65)分,关于这个常数的问题有两种解决方法:
- 可以把所有数全部乘上一个(2e9)转化为(long long)进行运算,算完在除回来,缺点是精度较低,容易写错
- 直接把(double)换成(longdouble)就行了,不过稍微慢点
看了看排行榜,好像有(O(n))的做法?
代码:
#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define qmax(x,y) (x=max(x,y))
#define qmin(x,y) (x=min(x,y))
#define mp(x,y) make_pair(x,y)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
inline int read(){
int ans=0,fh=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
ans=ans*10+ch-'0',ch=getchar();
return ans*fh;
}
const ll maxn=1e4+100,w=2e9;
int n,k;
ll p,g[maxn],f[maxn],pp[maxn];
double php;
int main(){
// freopen("nh.in","r",stdin);
// freopen("zhy.out","w",stdout);
scanf("%lf%d%d",&php,&n,&k);
if(php==0){
printf(n==1?"1
":"0
");
return 0;
}
p=php*w;
int x=k-1;
f[1]=1*w,g[0]=w-p,g[1]=p;
if(x==1)
for(int j=0;j<=n;j++)
pp[j]=g[j];
for(int i=2;i<=n;i++){
int j;
for(j=1;j<i-3;j+=4){
f[i]+=f[j]*g[i-j]/w;
f[i]+=f[j+1]*g[i-j-1]/w;
f[i]+=f[j+2]*g[i-j-2]/w;
f[i]+=f[j+3]*g[i-j-3]/w;
}
for(;j<i;j++) f[i]+=f[j]*g[i-j]/w;
(f[i]*=w-p)/=w;
for(j=i;j>=4;j-=4){
g[j]=(g[j-1]*p+g[j]*(w-p))/w;
g[j-1]=(g[j-2]*p+g[j-1]*(w-p))/w;
g[j-2]=(g[j-3]*p+g[j-2]*(w-p))/w;
g[j-3]=(g[j-4]*p+g[j-3]*(w-p))/w;
}
for(;j;j--) g[j]=(g[j-1]*p+g[j]*(w-p))/w;
(g[0]*=w-p)/=w;
if(i==x)
for(int j=0;j<=n;j++)
pp[j]=g[j];
(f[i]*=w)/=w-g[0];
}
if(x==0){
printf("%.12lf
",f[n]*1.0/w);
return 0;
}
double ans=0;
for(int i=0;i<=x;i++){
ans+=pp[i]*f[n-i]/w;
}
printf("%.12lf
",ans*1.0/w);
return 0;
}