BZOJ-4318 OSU! 概率DP

题目链接:BZOJ-4318 OSU!

题目

Description

osu 是一款群众喜闻乐见的休闲软件。
我们可以把osu的规则简化与改编成以下的样子:
一共有n次操作,每次操作只有成功与失败之分,成功对应1,失败对应0,n次操作对应为1个长度为n的01串。在这个串中连续的 X个1可以贡献X^3 的分数,这x个1不能被其他连续的1所包含(也就是极长的一串1,具体见样例解释)
现在给出n,以及每个操作的成功率,请你输出期望分数,输出四舍五入后保留1位小数。

Input

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

Output

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

Sample Input

3 
0.5 
0.5 
0.5 

Sample Output

6.0 

HINT

【样例说明】

000分数为0,001分数为1,010分数为1,100分数为1,101分数为2,110分数为8,011分数为8,111分数为27,总和为48,期望为48/8=6.0

N<=100000

解析

codeforces上有一道很像的题目:Let’s Play Osu!。那道题目里连续 1 1 1产生的是长度平方和收益,比这个要稍微简单一些,不过了解了思路后都是一样的。
首先,关于立方和。其实很简单啦,要是要求的是平方和,那我们可以利用 ( x + 1 ) 2 = x 2 + 2 ∗ x + 1 (x+1)^2=x^2+2*x+1 (x+1)2=x2+2x+1实现递推,类似的,立方和则是用 ( x + 1 ) 3 = x 3 + 3 ∗ x 2 + 3 ∗ x + 1 (x+1)^3=x^3+3*x^2+3*x+1 (x+1)3=x3+3x2+3x+1实现递推。关键在于每次实际上只有最后一段连续 1 1 1长度是变化的,直接这样递推会导致前面的连续段也出现改变。怎么办呢?
拿到题目后一个比较朴素的想法:开一个 n 2 n^2 n2的数组 d p [ i ] [ j ] dp[i][j] dp[i][j]记录推到第 i i i位,最后一个连续段长度为 j j j的期望分数。但是 n n n的范围没有那么友好,那我们只能O(n)解决了。我们仔细想一下,既然我们递推过程缺少的是最后一个连续段的长度,那我们直接开O(n)的数组维护最后一个连续段的长度期望不就可以了吗?
所以我们可以开两个辅助数组维护以 i i i号元素结尾的最长段及其平方的期望(平方的期望不等于期望的平方 E ( x 2 ) − E 2 ( x ) = D ( x ) E(x^2)-E^2(x)=D(x) E(x2)E2(x)=D(x),D是方差),然后每一位都分0、1两种情况,于是有公式 d p [ i + 1 ] = ( 1 − p [ i ] ) ∗ d p [ i − 1 ] + p [ i ] ∗ ( d p [ i − 1 ] + 3 ∗ t p [ i − 1 ] + 3 ∗ f p [ i − 1 ] + 1 ) dp[i+1]=(1-p[i])*dp[i-1]+p[i]*(dp[i-1]+3*tp[i-1]+3*fp[i-1]+1) dp[i+1]=(1p[i])dp[i1]+p[i](dp[i1]+3tp[i1]+3fp[i1]+1),剩下的就是敲出来。
上代码:

#include <cstdio>
using namespace std;

const int maxn = 1e5 + 10;
double p[maxn], dp[maxn], tp[maxn], fp[maxn];

int main()
{
	int n;
	scanf("%d", &n);
	for(int i = 1; i <= n; i++)
		scanf("%lf", &p[i]);
	dp[0] = fp[0] = tp[0] = 0;
	for(int i = 1; i <= n; i++){
		fp[i] = (fp[i-1]+1)*p[i];
		tp[i] = (tp[i-1]+2*fp[i-1]+1)*p[i];
		dp[i] = (1-p[i])*dp[i-1]+p[i]*(dp[i-1]+3*tp[i-1]+3*fp[i-1]+1);
	}
	printf("%.1f\n", dp[n]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值