JZOJ6026. 【GDOI2019模拟2019.2.23】飞行棋

Description

在这里插入图片描述

Data Constraint

在这里插入图片描述

Solution

  • 由于数据的特殊性以及精度的局限性,我们可以考虑暴力枚举轮数,然后计算答案,这样的轮数是在可以接受的范围内的。
  • 设状态f[i][j]表示第i轮,从j出发到达终点的概率,这个十分好转移O(6n)。
  • 对于第i轮,棋子x获胜的概率,是前i-1轮没有结束游戏,且前x-1个棋子也没有到达终点时,自己到达了终点。
  • 设b[i]表示棋子i的到达终点的概率(在当前i轮后)。记一个后缀积s[i]表示上一轮的1-b[j](i<=j<=m)的积,再乘上前缀积t[i]表示这一轮的1-b[j](1<=j<=i)的积
  • 贡献就是s[i+1]* t[i-1] *这个棋子前几轮没有到终点 * b[i]。O(m)。
  • O(K*(6n+m))
  • 当游戏继续的概率小于某个阈值时就可以停了。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define eof 0.0000001
#define K 100000
#define maxn 160
#define maxm 21
#define db long double 
using namespace std;

int n,m,i,j,k,a[maxn],w[maxm];
db f[2][maxn],b[maxm],s[maxm],d,rem,sum,ans[maxm];

int main(){
	freopen("feixingqi.in","r",stdin);
	freopen("feixingqi.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++) scanf("%d",&a[i]);
	for(i=1;i<=m;i++) scanf("%d",&w[i]);
	if (m==1){
		printf("1.000000");
		return 0;
	}
	
	memset(b,0,sizeof(b));
	for(i=0;i<6;i++) f[0][n+i]=1;
	sum=1; int t=0; d=1./6;
	for(;sum-eof>0;){
		t++;
		int p=t&1,q=p^1;
		memset(f[p],0,sizeof(f[p]));
		for(i=1;i<n;i++) if (a[i]==i) for(j=1;j<=6;j++) 
			f[p][i]+=d*f[q][i+j];
		for(i=1;i<=n;i++) f[p][i]=f[p][a[i]];
		s[m+1]=1;
		for(i=m;i>=1;i--) s[i]=s[i+1]*(1-b[i]);
		sum=1;
		for(i=1;i<=m;i++) {
			ans[i]+=sum*s[i+1]*f[p][w[i]];
			b[i]=b[i]+f[p][w[i]];
			sum=sum*(1-b[i]);
		}
	}
	for(i=1;i<=m;i++) printf("%.6Lf
",ans[i]);
}
原文地址:https://www.cnblogs.com/DeepThinking/p/11700948.html