• [LnOI2019]加特林轮盘赌(DP,概率期望)


    [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;
    }
    
  • 相关阅读:
    redis 数据类型详解 以及 redis适用场景场合
    angular.js记录
    Python chr() 函数
    Python frozenset() 函数
    Python 字典 dict() 函数
    Python set() 函数
    Python tuple 函数
    Python eval() 函数
    Python repr() 函数
    Python str() 函数
  • 原文地址:https://www.cnblogs.com/nianheng/p/10506033.html
Copyright © 2020-2023  润新知