【BZOJ4318】OSU!

Description
osu 是一款群众喜闻乐见的休闲软件。
我们可以把osu的规则简化与改编成以下的样子:
一共有n次操作,每次操作只有成功与失败之分,成功对应1,失败对应0,n次操作对应为1个长度为n的01串。在这个串中连续的 个1可以贡献 的分数,这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

期望DP,和Easy那题一样
令f[i]为期望得分,g[i]期望长度,如果我们直接考虑把Easy的做法扩展到三次,会得到
f[i]=f[i1]+(3g[i1]2+3g[i1]+1)a[i]
要注意的是平方的期望不等于期望的平方,因此要单独求出平方的期望,不能直接乘方来做.

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define MAXN 100010
using namespace std;
int n;
double f[MAXN],g1[MAXN],g2[MAXN],a[MAXN],ans;
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        scanf("%lf",&a[i]);
        g1[i]=(g1[i-1]+1)*a[i];
        g2[i]=(g2[i-1]+2*g1[i-1]+1)*a[i];
        f[i]=f[i-1]+a[i]*(3*g2[i-1]+3*g1[i-1]+1);
    }
    printf("%.1f\n",f[n]);
}

以及Claris有神做法…
对于每个位置i,它成功时对答案的贡献为a[i]
对于两个位置i< j,它们中间都成功时,对答案的贡献为6a[i]a[i+1]…a[j-1]a[j]*(j-i)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define MAXN 100010
using namespace std;
int n;
double ans;
double f[MAXN],g[MAXN],a[MAXN];
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)  scanf("%lf",&a[i]),ans+=a[i];
    for (int i=1;i<=n;i++)  g[i]=(g[i-1]+1)*a[i],f[i]=(f[i-1]+g[i-1])*a[i],ans+=f[i]*6;
    printf("%.1f\n",ans);
}

你可能感兴趣的:(期望DP)