【题解】P1654 OSU!
post on 2022.5.4:
昨天艾教重新很详细的讲了一遍这道题,之前就感觉这道题并没有完全理解。
昨天艾教讲了之后我才茅塞顿开。这里记录一下艾教的推导过程,一是让我再次捋一遍思路,更加清楚;二是希望我以后见到类似的题目之后,能有所进步,而不是像刚开始一样手足无措。
题目链接
P1654 OSU!
思路分析
解法一
用 Pi,j 表示现在走到了第 i 个操作,连续成功了 j 次。
Ei(x) 表示现在走到了第 i 个操作,期望连续成功多少次。
那么根据期望的定义有:
Ei(x)=j=0∑∞j×Pi,j 同理,
Ei(x2)=j=0∑∞j2×Pi,jEi(x3)=j=0∑∞j3×Pi,j 考虑递推式。
假设每次的成功概率是 ki。
对于第 i 位,我们有 ki 的概率成功,那么假设到第 i−1 位连续成功了 j 次,那么第 i 位上若成功,就连续成功了 j+1 次,因此同样根据期望的定义我们可以将 Ei(x) 用 Pi−1,j 表示:
Ei(x)=ki×j=0∑∞(j+1)Pi−1,j=ki×(j=0∑∞j⋅Pi−1,j+j=0∑∞Pi−1,j)=ki×(1+Ei−1(x)) 所以对于二次:
Ei(x2)=ki×j=0∑∞(j+1)2⋅Pi−1,j=ki×j=0∑∞(j2+2j+1)⋅Pi−1,j=ki×(j=0∑∞j2⋅Pi−1,j+j=0∑∞2j⋅Pi−1,j+j=0∑∞Pi−1,j)=ki×(Ei−1(x2)+2Ei−1(x)+1) 同理,
Ei(x3)=ki×(Ei−1(x3)+3Ei−1(x2)+3Ei−1x+1) 我们可以发现这玩意其实满足二项式定理。
那么其实可以拓展到 n 次期望(一会再说)。
考虑统计答案。
可以发现:
ansi=(1−ki+1)×Ei(x3) 这个式子如何理解呢?
对于一个答案序列,我们最后统计答案时只计算每一段连续的 1 中最后一个位置的答案。
举个例子:当我们最后序列是 0011101101
这个序列时,我们最后答案计算方法是:33+23+13=36。相当于是序列第 5 个位置为整个答案贡献了 23,第 8 个位置贡献了 23,第 10 个位置贡献了 13。
也就是说,实际上只有每一段的最后一个位置对答案有贡献。而其它位置,可以看做是对答案无贡献的。
那么在具体的式子里面如何判断一个位置是不是一段连续的 1 的最后一个位置呢?
显然对于一个位置 i,如果有贡献,那么第 i+1 个位置一定为 0。
那么就很显然了。根据期望的定义,用第 i+1 个位置为 0 的概率乘上第 i 个位置的期望即为 ansi。
解法二
我们首先从一个例子引入。
假设我们最终的答案序列是 1110110
,那么答案显然为 33+23=35,对于上一种解法,我们是将它看成看成每一段连续的 1 的最后一个位置对答案的贡献求和。
在这里,我们可以换一种思路:将每一段连续的 1 的最后一个位置的贡献分配到这一段包含连续的 1 的所有位置上去。
比如:对于上面这个例子,我们可以看做:第一个位置对答案贡献了 1,第二个位置对答案贡献了 7,第三个位置对答案贡献了 19。
也就是说,对于一段连续的 1 中的第 x+1 个 1,对答案贡献了 (x+1)3−x3。根据期望的定义,每一个是 1 位置 i 对答案的贡献就是:
j=0∑∞Pi−1,j((j+1)3−j3)=j=0∑∞Pi−1,j(3j3+3j+1)=j=0∑∞3j3⋅Pi−1,j+j=0∑∞3j⋅Pi−1,j+j=0∑∞Pi−1,j=3Ei−1(x2)+3Ei−1(x)+1 所以
ansi=ki(3Ei−1(x2)+3Ei−1(x)+1) 拓展
根据一次,二次,三次期望,我们可以拓展到 k 次期望。
通过解法一中的递推式我们可以发现:
Ei(xk)=ki×(Ei−1x+1)k 二项式定理解决即可。
代码实现
#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];
}
cout<<fixed<<setprecision(1)<<ans[n]<<endl;
return 0;
}
三倍经验
P1365 WJMZBMR打osu! / Easy
CF235B Let's Play Osu!
ARC#157 C
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?