【2020牛客NOIP赛前集训营-普及组】飞行棋
题目
题目链接:https://ac.nowcoder.com/acm/contest/7612/D
思路
显然要棋子在 \([1,d)\) 和 \([d,n]\) 的时候分开做。
当棋子在 \([1,d)\) 的时候,假设现在在位置 \(p\),骰子扔到的是 \(x\) 点,那么棋子可能会变到 \(p-x\) 或 \(x-p\) 的位置。
发现只有当 \(x\) 恰好等于 \(p\) 的时候才可以结束,否则 \(p\) 将变为依然在 \([1,d)\) 中的另一个位置 \(p'\)。那么其实等价于在 \(1\sim d-1\) 中一直随机一个数,直到随机到 \(1\) 时停止,求期望次数。
设期望 \(s\) 次扔到点 \(1\),那么有
\[s=\frac{d-2}{d-1}(s+1)+\frac{1}{d-1}
\]
解方程得到 \(s=d-1\)。
那么当棋子在 \([1,d)\) 时,期望 \(d-1\) 次才会回到点 \(0\)。
那么当棋子所在位置 \(p\geq d\) 时,由于只会不停往左走,所以可以设 \(f[i]\) 表示点 \(i\) 开始期望多少次可以回到点 \(0\),这样是没有后效性的。
时间复杂度 \(O(Tn)\)。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=100010;
int Q,n,d;
double f[N],sum[N];
int main()
{
scanf("%d",&Q);
while (Q--)
{
scanf("%d%d",&n,&d);
f[0]=sum[0]=1;
for (int i=1;i<d;i++)
f[i]=d-1,sum[i]=sum[i-1]+f[i];
for (int i=d;i<=n;i++)
{
f[i]=(sum[i-1]-sum[i-d]+d-1)/d+f[i-d]/d;
sum[i]=sum[i-1]+f[i];
}
printf("%.2lf\n",f[n]);
}
return 0;
}