【题解】P1654 OSU!(组合数学,概率,期望)
【题解】P1654 OSU!
post on 2022.5.4:
昨天艾教重新很详细的讲了一遍这道题,之前就感觉这道题并没有完全理解。
昨天艾教讲了之后我才茅塞顿开。这里记录一下艾教的推导过程,一是让我再次捋一遍思路,更加清楚;二是希望我以后见到类似的题目之后,能有所进步,而不是像刚开始一样手足无措。
题目链接
思路分析
解法一
用 \(P_{i,j}\) 表示现在走到了第 \(i\) 个操作,连续成功了 \(j\) 次。
\(E_i(x)\) 表示现在走到了第 \(i\) 个操作,期望连续成功多少次。
那么根据期望的定义有:
同理,
考虑递推式。
假设每次的成功概率是 \(k_i\)。
对于第 \(i\) 位,我们有 \(k_i\) 的概率成功,那么假设到第 \(i-1\) 位连续成功了 \(j\) 次,那么第 \(i\) 位上若成功,就连续成功了 \(j+1\) 次,因此同样根据期望的定义我们可以将 \(E_i(x)\) 用 \(P_{i-1,j}\) 表示:
所以对于二次:
同理,
我们可以发现这玩意其实满足二项式定理。
那么其实可以拓展到 \(n\) 次期望(一会再说)。
考虑统计答案。
可以发现:
这个式子如何理解呢?
对于一个答案序列,我们最后统计答案时只计算每一段连续的 1 中最后一个位置的答案。
举个例子:当我们最后序列是 0011101101
这个序列时,我们最后答案计算方法是:\(3^3+2^3+1^3=36\)。相当于是序列第 5 个位置为整个答案贡献了 \(2^3\),第 8 个位置贡献了 \(2^3\),第 10 个位置贡献了 \(1^3\)。
也就是说,实际上只有每一段的最后一个位置对答案有贡献。而其它位置,可以看做是对答案无贡献的。
那么在具体的式子里面如何判断一个位置是不是一段连续的 1 的最后一个位置呢?
显然对于一个位置 \(i\),如果有贡献,那么第 \(i+1\) 个位置一定为 0。
那么就很显然了。根据期望的定义,用第 \(i+1\) 个位置为 0 的概率乘上第 \(i\) 个位置的期望即为 \(ans_i\)。
解法二
我们首先从一个例子引入。
假设我们最终的答案序列是 1110110
,那么答案显然为 \(3^3+2^3=35\),对于上一种解法,我们是将它看成看成每一段连续的 1 的最后一个位置对答案的贡献求和。
在这里,我们可以换一种思路:将每一段连续的 1 的最后一个位置的贡献分配到这一段包含连续的 1 的所有位置上去。
比如:对于上面这个例子,我们可以看做:第一个位置对答案贡献了 1,第二个位置对答案贡献了 7,第三个位置对答案贡献了 19。
也就是说,对于一段连续的 1 中的第 \(x+1\) 个 1,对答案贡献了 \((x+1)^3-x^3\)。根据期望的定义,每一个是 1 位置 \(i\) 对答案的贡献就是:
所以
拓展
根据一次,二次,三次期望,我们可以拓展到 \(k\) 次期望。
通过解法一中的递推式我们可以发现:
二项式定理解决即可。
代码实现
//luoguP1654
#include<iostream>
#include<cstdio>
#include<iomanip>
using namespace std;
const int maxn=1e5+10;
double p[maxn];
double s1[maxn],s2[maxn],s3[maxn],ans[maxn];
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
return x*f;
}
int main()
{
int n;
n=read();
for(int i=1;i<=n;i++)cin>>p[i];
for(int i=1;i<=n;i++)
{
s1[i]=(s1[i-1]+1)*p[i];
s2[i]=(s2[i-1]+2*s1[i-1]+1)*p[i];
s3[i]=(s3[i-1]+3*s2[i-1]+3*s1[i-1]+1)*p[i];
ans[i]=ans[i-1]+(1-p[i+1])*s3[i];//解法一
//ans[i]=ans[i-1]+(3*s2[i-1]+3*s1[i-1]+1)*p[i];//解法二
}
cout<<fixed<<setprecision(1)<<ans[n]<<endl;
return 0;
}