jzoj6026 飞行棋 dp

版权声明:虽然是个蒟蒻但是转载还是要说一声的哟 https://blog.csdn.net/jpwang8/article/details/87896000

Description


小G在玩飞行棋。这个飞行棋与一般的飞行棋相比,规则要简单得多。棋盘上一共有从左到右n个格子,按1到n标号。m个玩家各持有一个棋子。棋子第一个到达第n格的玩家胜利。每个玩家轮流投掷6面的骰子,投出几点就把自己的棋子往右移动几步。当棋子被移动到某些格子时,棋子会被传送到其他格子。如果棋子被移动到第i格,若ai=i ,则棋子仍然在第i格;否则棋子会被传送到第ai格。棋子每次按骰子投出的数字移动时,是一次性移动了若干格,即棋子不会在中途被传送走,只可能在移动完后被传送走。不同玩家的棋子之间互不影响。

现在小G告诉了你m个玩家棋子所在位置。现在开始按1号玩家到m号玩家的顺序依次扔骰子。小G想知道每个玩家获胜的概率。

Solution


这道题妙在输出实数,这意味着我们可以偷懒
设f[i,j]表示从i开始走了j步到n的概率,转移比较显然。那么输出答案就枚举赢家走的步数,强制编号小的人步数比他大,其余人步数不比他少就行了。这个可以用前缀后缀积来做

第一次挂了是因为只做了5w轮,实测我的代码做50w轮才能比较优秀地收敛,不知道那些20w轮的大爷有啥黑科技。。
人紬常数大,愁死我了

Code


#pragma GCC optimize(3)
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#define rep(i,st,ed) for (register int i=st;i<=ed;++i)
#define drp(i,st,ed) for (register int i=st;i>=ed;--i)

typedef double db;
const int N=550005;

db f[2][159],s1[25][N+5],s2[25][N+5],g[25][N+5];

int a[159],st[25];

int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

int main(void) {
	freopen("feixingqi.in","r",stdin);
	freopen("feixingqi.out","w",stdout);
	int n=read(),m=read();
	if (m==1) return 0&puts("1.000000");
	rep(i,1,n) a[i]=read();
	rep(i,1,m) st[i]=read();
	rep(i,n+1,n+6) a[i]=n;
	f[0][n]=1;
	db s=1.0/6.0;
	rep(i,1,N-1) {
		rep(j,0,n) f[i&1][j]=0;
		rep(j,1,n-1) rep(k,1,6) {
			f[i&1][j]+=s*f[(i-1)&1][a[j+k]];
		}
		rep(j,1,m) g[j][i]+=f[i&1][st[j]];
	}
	drp(i,N-1,0) {
		s1[0][i]=s2[m+1][i]=1;
		rep(j,1,m) g[j][i]+=g[j][i+1];
		rep(j,1,m) s1[j][i]=s1[j-1][i]*g[j][i];
		drp(j,m,1) s2[j][i]=s2[j+1][i]*g[j][i];
	}
	rep(i,1,m) {
		db ans=0;
		rep(j,1,N-1) ans+=s1[i-1][j+1]*(g[i][j]-g[i][j+1])*s2[i+1][j];
		printf("%.6lf\n", ans);
	}
	// std:: cout << (int) sizeof(f)<< std:: endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/jpwang8/article/details/87896000