P1654 OSU!
似乎是一道蛮经典的期望递推题。
洛谷上的题解看不懂,老师讲的也很简略。只有自己推起来感觉很复杂。。。心累。我尽量把这篇题解写得严谨些。
题意简述
一共有 次操作,第 次操作()成功的概率是 。成功记作 ,失败记作 ,那么所有操作的结构依次连起来就可以得到一个 串。在这个串中,每个极大的、连续的 个 会对分数产生 的贡献,每个全为 的子段的贡献线性累加。
求:贡献总和的期望 。
范围:。
思路
考虑线性递推。
-
我们把以位置 结尾的一串 的长度记为 ,考虑如何从 的情况推出 的情况。
-
让我们先试着求长度一次方的期望。我们把 的期望记为 。在这个位置 ,我们向串尾追加第 个操作结果,有两种情况:
-
有 的概率第 次操作成功。成功后 的期望为 。(也就是 串的长度 )这里注意一个细节:我们写成 而不是 ,因为它们含义不同,尽管它们数值上相等。稍后我们推 的期望时就不能混用了。
-
另外 的概率第 次操作失败。失败后串尾就没有 了,所以此时 的期望为 。
所以:
由期望的线性性,拆开 :
这里常数 的期望 当然就是 了,不然还能有别的取值吗。于是 。进一步化简,得:
-
-
类似地,记 的期望为 。我们尝试求一下长度二次方的期望的递推式:
请注意: 此处是 而不是 ,写成后面那个就推不出来了。(而且也是错的)。
化简 ,依旧是利用期望的线性性:
化简整个式子:
-
类似地,记 的期望为 。那么长度三次方的期望的递推式:
化简(过程略):
期望推过瘾了,那我们怎么求答案呢?我们假设 上的分数的期望为 ,那么 就是答案了,接下来考虑如何递推:
-
有 的概率第 次操作成功,那么 串继续累加,。这一部分和长度三次方的期望是一样的(只不过我们把 换成了 )。
-
另外 的概率第 次操作失败,那么期望不变, 。注意,这里就和上面长度三次方的递推式不同了。因为上面我们规定 是以 结尾的 串的长度,如果操作失败,长度和期望都清零了。但是这里我们是要统计累积的期望分数,所以期望不清零。
加起来就得到:
结合 ,就容易设计递推了。
编码
其实编码非常简单,本题难度几乎全在于推式子。
用时 81 ms 内存 6.36 MB
/*
* P1654 OSU!
* 数学期望
*/
#include <bits/stdc++.h>
using namespace std;
typedef long double Float;
const int MAXN = 1e5;
Float p[MAXN + 1], E1[MAXN + 1], E2[MAXN + 1], ans[MAXN + 1];
int main() {
int n;
cin >> n;
for (int k = 1; k <= n; k++) {
cin >> p[k];
E1[k] = p[k] * (E1[k - 1] + 1);
E2[k] = p[k] * (E2[k - 1] + 2 * E1[k - 1] + 1);
ans[k] = ans[k - 1] + p[k] * (3 * E2[k - 1] + 3 * E1[k - 1] + 1);
}
printf("%.1Lf", ans[n]);
return 0;
}
补充
请注意: ,即三次方的期望 期望的三次方。这也解释了我们为什么要大费周章地推式子,而不是直接推出 然后取三次方。
为了避免这个混淆,上面我不辞辛苦地把一个个期望都严格写成 而不是 。TwT