OSU!

题目描述
osu 是一款群众喜闻乐见的休闲软件。

我们可以把osu的规则简化与改编成以下的样子:

一共有n次操作,每次操作只有成功与失败之分,成功对应1,失败对应0,n次操作对应为1个长度为n的01串。在这个串中连续的 X个1可以贡献X^3 的分数,这x个1不能被其他连续的1所包含(也就是极长的一串1,具体见样例解释)

现在给出n,以及每个操作的成功率,请你输出期望分数,输出四舍五入后保留1位小数。

输入格式
第一行有一个正整数n,表示操作个数。

接下去n行每行有一个[0,1]之间的实数,表示每个操作的成功率。

输出格式
只有一个实数,表示答案。答案四舍五入后保留1位小数。

样例
样例输入
3
0.5
0.5
0.5
样例输出
6.0
数据范围与提示
000分数为0,001分数为1,010分数为1,100分数为1,101分数为2,110分数为8,011分数为8,111分数为27,总和为48,期望为48/8=6.0

N<=100000

很显然啊,没推出来
(同Elaina进行了激烈的讨论,未果,遂看题解)

既然看了题解,那么就说说过程
比较容易想到的就是利用dp(思想)我们令f[i]为前i位的期望之和,很显然,对于第i位,会有1和0两种选择:
0的话就可以直接从前面(f[i-1])继承
1的话,就比较难办,因为我不知道从尾部开始连续1字串的长度,所以我就想去记录一下长度,但又一想,情况多了去了,怎么记录,况且概率各不相同,然后就卡住了(实际上我还想去预处理出\(x^{3}\)的,但被Elaina悬崖勒马了),最后看题解去了

开始正题

我们先不从3次方开始,先从一次的开始,对于第i位选1的期望值,那么会有
\(x^{1}[i]=(x^{1}[i-1]+1)*p[i]\)

那么对于二次的呢
根据公式:\((x+1)^{2}=x^{2}+2x+1\)
可知:
\(x^{2}[i]=(x^{2}[i-1]+2*x^{1}[i-1]+1)*p[i]\)

那么三次的也就有了
根据公式:\((x+1)^{3}=x^{3}+3x^{2}+3x^{1}+1\)
可知:
\(x^{3}[i]=(x^{3}[i-1]+3*x^{2}[i-1]+3*x^{1}[i-1]+1)*p[i]\)

到这里就有一个问题,为什么仅用算1的情况,那0呢?
实际上,根据上面的口胡,会发现,当选中1时,采用上面的公式,但对于0的话,它没有任何作用,也就是说,它可以直接从\(x^{3}[i-1]\)转移过来
因此,本题的真正转移方程实际是:

\(f[i]=(f[i-1]+3*x^{2}[i-1]+3*x^{1}[i-1]+1)*p[i]+f[i-1]*(1-p[i])\)

fixed:注意,这里的f不能等同于上面的\(x^{3}\),他们两个意义不同,\(x^{3}[i]\)表示选中1的贡献,而f是包含了两种情况的总期望

fixed:实际上写完博客时并没有理解这个思路,后面看了这个才理解,概率有线性性质,对于一般的期望可以直接根据这个性质\(f[i]=(f[i-1]+w)*p[i]\)求值,但对于本题来说,x3不符合线性性质,因此要从一维拓展到二维,二维拓展到三维,来解决x3转移的问题,且一维二维均为抽中1时的转移,如果是0那么直接赋0即可(例如easy中的x,直接给x1赋0就行)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+100;
int n;
double f[N],p[N],x1[N],x2[N];
int cnt=0;
int main(){
	cin>>n;
	for(int i=1;i<=n;i++) cin>>p[i];
	for(int i=1;i<=n;i++){
		x1[i]=(x1[i-1]+1)*p[i];
		x2[i]=(x2[i-1]+2*x1[i-1]+1)*p[i];
		f[i]=(f[i-1]+3*x2[i-1]+3*x1[i-1]+1)*p[i]+f[i-1]*(1-p[i]);
	}
	cout<<setprecision(1)<<fixed<<f[n]<<endl;
}

最后贴一张深有感触的图
image

posted @ 2024-05-27 16:38  shaoyufei  阅读(14)  评论(0编辑  收藏  举报