飞行棋

嫅朑磃淥(解题思路)
我们来进行分类讨论:
当 i<di<di<d 时
设期望 sss 次扔到点 111 ,那么有:
s=d−2d−1(s+1)+1d−1s=\frac{d-2}{d-1}(s+1)+\frac{1}{d-1}s=d−1d−2(s+1)+d−11
解得:
s=d−1s=d-1s=d−1
所以 f[1f[1f[1~d−1]d-1]d−1] 的值就为 d−1d-1d−1
当 i≥di≥di≥d 时
我们有 1d\frac{1}{d}d1 的概率走到 i−di-di−d~i−1i-1i−1 的所有点,且走到 i−di-di−d 不需要回合,那么有:
f[i]=∑j=i−d+1i−1f[j]+1d+f[i−d]df[i] = \frac{\sum_{j=i-d+1}^{i-1}f[j]+1}{d}+\frac{f[i-d]}{d}f[i]=d∑j=i−d+1i−1f[j]+1+df[i−d]
但是呢,如果这样的话时间复杂度是 O(Tnd)O(Tnd)O(Tnd) ,而 2≤d,n≤1000002≤d,n≤1000002≤d,n≤100000 ,所以这样肯定会爆。我们就用前缀和 sss 来优化,表达式就变成了这样:
f[i]=s[i−1]−s[i−d]+d−1d+f[i−d]df[i]=\frac{s[i-1]-s[i-d]+d-1}{d}+\frac{f[i-d]}{d}f[i]=ds[i−1]−s[i−d]+d−1+df[i−d]
于是时间复杂度就很优秀的达到了 O(Tn)O(Tn)O(Tn)
code
#include<iostream>
#include<cstdio>
using namespace std;
int T;
int n,d;
double f[100010],s[100010];
int main()
{
cin>>T;
while(T--)
{
scanf("%d%d",&n,&d);
f[0]=s[0]=1;
for(int i=1;i<d;i++)
f[i]=d-1,s[i]=s[i-1]+f[i];
for(int i=d;i<=n;i++)
{
f[i]=(s[i-1]-s[i-d]+d-1)/d+f[i-d]/d;
s[i]=s[i-1]+f[i];
}
printf("%.2lf\n",f[n]);
}
}
本文介绍了一种计算飞行棋游戏中从任意位置到达终点期望步数的方法。通过分类讨论和使用前缀和进行优化,将时间复杂度降低到了O(Tn),适用于2到100000范围内的参数。
370

被折叠的 条评论
为什么被折叠?



