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]);
}