【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;
}
posted @ 2020-10-28 11:41  stoorz  阅读(226)  评论(2编辑  收藏  举报