飞行棋
嫅朑磃淥(解题思路)
我们来进行分类讨论:
当
i
<
d
i<d
i<d 时
设期望
s
s
s 次扔到点
1
1
1 ,那么有:
s
=
d
−
2
d
−
1
(
s
+
1
)
+
1
d
−
1
s=\frac{d-2}{d-1}(s+1)+\frac{1}{d-1}
s=d−1d−2(s+1)+d−11
解得:
s
=
d
−
1
s=d-1
s=d−1
所以
f
[
1
f[1
f[1~
d
−
1
]
d-1]
d−1] 的值就为
d
−
1
d-1
d−1
当
i
≥
d
i≥d
i≥d 时
我们有
1
d
\frac{1}{d}
d1 的概率走到
i
−
d
i-d
i−d~
i
−
1
i-1
i−1 的所有点,且走到
i
−
d
i-d
i−d 不需要回合,那么有:
f
[
i
]
=
∑
j
=
i
−
d
+
1
i
−
1
f
[
j
]
+
1
d
+
f
[
i
−
d
]
d
f[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
(
T
n
d
)
O(Tnd)
O(Tnd) ,而
2
≤
d
,
n
≤
100000
2≤d,n≤100000
2≤d,n≤100000 ,所以这样肯定会爆。我们就用前缀和
s
s
s 来优化,表达式就变成了这样:
f
[
i
]
=
s
[
i
−
1
]
−
s
[
i
−
d
]
+
d
−
1
d
+
f
[
i
−
d
]
d
f[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
(
T
n
)
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]);
}
}