本题很难!!!所以单独拎出来讲
显然要棋子在
[
1
,
d
)
[1,d)
[1,d) 和
[
d
,
n
]
[d,n]
[d,n] 的时候分开做。
当棋子在
[
1
,
d
)
[1,d)
[1,d) 的时候,假设现在在位置
p
p
p,骰子扔到的是
x
x
x 点,那么棋子可能会变到
p
−
x
p−x
p−x 或
x
−
p
x−p
x−p 的位置。
发现只有当
x
x
x 恰好等于
p
p
p 的时候才可以结束,否则
p
p
p 将变为依然在
[
1
,
d
)
[1,d)
[1,d) 中的另一个位置
p
p
p′。那么其实等价于在
1
∼
d
−
1
1∼d−1
1∼d−1 中一直随机一个数,直到随机到
1
1
1 时停止,求期望次数。
设期望
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。
那么当棋子在
[
1
,
d
)
[1,d)
[1,d) 时,期望
d
−
1
d−1
d−1 次才会回到点 0。
那么当棋子所在位置
p
≥
d
p≥d
p≥d 时,由于只会不停往左走,所以可以设
f
[
i
]
f[i]
f[i] 表示点
i
i
i 开始期望多少次可以回到点
0
0
0,这样是没有后效性的。
得到DP转移方程:
f
[
i
]
=
(
∑
j
=
i
−
d
+
1
i
−
1
f
[
j
]
+
1
)
+
f
[
i
−
d
]
d
f[i] = (\sum_{j=i-d+1}^{i-1}{f[j]+1})+\frac{f[i-d]}{d}
f[i]=(j=i−d+1∑i−1f[j]+1)+df[i−d]
前缀和优化求和即可
时间复杂度
O
(
T
n
)
O(Tn)
O(Tn)。
以上的部分解释载自zycdalao的博客
代码
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
double sum[1000010],f[1000010];
int T,n,d;
int main()
{
cin>>T;
while(T--)
{
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;
}