sol:
提供一种比较简洁和巧妙的做法(二维状态高斯消元就不说了吧 qwq)
题解在代码注释里了。
#include<bits/stdc++.h>
#define db double
using namespace std;
const int N=1e5+5;
//纯数学推导:
//设 dp[i] 表示当前分数为 i ,到达 20 的期望步数
//根据定义得到
//dp[0] = dp[0] * q + dp[1] * p + 1
//所以
//dp[0] = dp[1] + 1 / p
//同理 dp[1] = dp[0] * q + dp[2] * p + 1
//即 dp[1] = (dp[1] + 1) * q + dp[2] * p + 1
//所以 dp[0] = 1/{p^2} + 1/p + dp[2]
//以此类推,对于 0<i<=n ,都有 dp[0] = dp[i] + ti ,那么对于 i=n, 有 dp[i] = dp[i-2] * q + dp[i+1] * p + 1
//即 dp[0] - ti = (dp[0] - t_{i-2}) * q + dp[i+1] * p +1
//我们要研究的式子左边是 dp[0] ,而右边是系数为 1 的 dp[i+1] 和一个常数,所以移项可得:
//p * dp[0] = -t_{i-2} * q + ti + dp[i+1] * p + 1
//dp[0] = -t{i-2} * q / p + ti / p + 1/p + dp[i+1]
//所以 t{i+1} = -t{i-2} * q / p + ti / p + 1/p
//又很容易发现两个账号打的次数是可以分开算的,且最终状态一定一个为 19,另一个为 20,所以答案就是 t19 + t20
//当然其他换元的方法也能通过
//关键在于能否推出数列递推式(本质是数列 eg. ai = a_{i-1} +a_{i+1} 将下标进行加减就可以得到不带后效性的递推式 很神奇 qwq)
db p,q,t[N];
int main() {
while(cin>>p) {
q=1-p;
t[1]=1.0/p;
t[2]=1.0/p+1.0/(p*p);
for(int i=2;i<20;i++) {
t[i+1]=-t[i-2]*q/p+t[i]/p+1.0/p;
}
printf("%lf\n",t[19]+t[20]);
}
}