Codeforces 235B Let's Play Osu! (概率dp求期望+公式变形)


B. Let's Play Osu!
time limit per test:2 seconds
memory limit per test:256 megabytes

You're playing a game called Osu! Here's a simplified version of it. There are n clicks in a game. For each click there are two outcomes: correct or bad. Let us denote correct as "O", bad as "X", then the whole play can be encoded as a sequence of n characters "O" and "X".

Using the play sequence you can calculate the score for the play as follows: for every maximal consecutive "O"s block, add the square of its length (the number of characters "O") to the score. For example, if your play can be encoded as "OOXOOOXXOO", then there's three maximal consecutive "O"s block "OO", "OOO", "OO", so your score will be 22 + 32 + 22 = 17. If there are no correct clicks in a play then the score for the play equals to 0.

You know that the probability to click the i-th (1 ≤ i ≤ n) click correctly is pi. In other words, the i-th character in the play sequence has pi probability to be "O", 1 - pi to be "X". You task is to calculate the expected score for your play.

Input

The first line contains an integer n (1 ≤ n ≤ 105) — the number of clicks. The second line contains n space-separated real numbers p1, p2, ..., pn (0 ≤ pi ≤ 1).

There will be at most six digits after the decimal point in the given pi.

Output

Print a single real number — the expected score for your play. Your answer will be considered correct if its absolute or relative error does not exceed 10 - 6.

Sample test(s)
Input
3
0.5 0.5 0.5
Output
2.750000000000000
Input
4
0.7 0.2 0.1 0.9
Output
2.489200000000000
Input
5
1 1 1 1 1
Output
25.000000000000000
Note

For the first example. There are 8 possible outcomes. Each has a probability of 0.125.

  • "OOO"  →  32 = 9;
  • "OOX"  →  22 = 4;
  • "OXO"  →  12 + 12 = 2;
  • "OXX"  →  12 = 1;
  • "XOO"  →  22 = 4;
  • "XOX"  →  12 = 1;
  • "XXO"  →  12 = 1;
  • "XXX"  →  0.

So the expected score is


题目链接:http://codeforces.com/contest/235/problem/B


题目大意:长度为n的序列,每个位置上出现O的概率为pi,得分为连续k个O的k^2分,现在求得分的期望


题目分析:n^2 = 2 * C(2, n) + n,问题转化对于每段连续的O,求其子段长度大于等于2的段数*2再加上连续O的个数,比如OOO有三个O,两个OO,一个OOO,所以分数为2*3+3 = 9 = 3^3,这样问题就简单了,考虑每个点i,其能构成的长度为i,i-1,i-2,...,2,对应的概率为p1*p2*...*pi,p2*p3*...*pi,...,pi-1*pi

为方便表示,设(j, i)为p[j]*p[j+1]*...*p[i-1]*p[i],令dp[i] = Σ(j,i)其中1 <= j < i
则有dp[0] = 0,dp[2] = p[1]*p[2],dp[3] = p[1]* p[2] *p[3] + p[2]*p[3],可以看出

dp[i] = (dp[i - 1] + p[i - 1]) * p[i],dp[1]+dp[2]+dp[3] = p[1]*p[2] + p[2]*p[3] + p[1]*p[2]*p[3],发现正好是长度为3时,连续个数大于等于2的情况,所以之后我们把每个dp[i]的值乘2累加就是C(2,n)*2对答案的贡献,再加上Σpi,即每个O对答案的贡献就是最终的答案,所以可以写成


#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int const MAX = 1e5 + 5;
double dp[MAX], p[MAX];

int main()
{
    int n;
    scanf("%d", &n);
    double ans = 0;
    for(int i = 1; i <= n; i++)
    {
        scanf("%lf", &p[i]);
        ans += p[i];
    }
    for(int i = 2; i <= n; i++)
    {
        dp[i] = (dp[i - 1] + p[i - 1]) * p[i];
        ans += 2 * dp[i];
    }
    printf("%.10f\n", ans);
}

仔细观察dp方程,dp[1] = 0,dp[i] = (dp[i - 1] + p[i - 1]) * p[i],这个递推式可以直接通过变量的循环实现,具体方法是先设一个tmp,在第i次循环时tmp就代表p[i],再设一个now表示多加一位时,新增的连续O长度大于1的情况,然后直接ans += 2 * now + tmp即长度大于1的每个要乘2再加本次添加的那个长度为1的

比如n=3时有p1,p2,p3计算过程为

i=1,tmp=p1,now=0,ans += p1,now = p1

i=2,tmp=p2,now=p1*p2,ans += 2*now + tmp = 2p1p2 + p2 + p1,now = p2 + p1p2

i=3,tmp=p3,now=p2p3 + p1*p2*p3,ans += 2*now + tmp = 2p2p3 + 2p1p2p3 + p3 + 2p1p2 + p2 + p1

所以ans = p1 + p2 + p3 + 2p1p2 + 2p2p3 + 2p1p2p3,这正好是我们定义的答案,所以还可以不开数组


#include <cstdio>

int main()
{
    int n;
    scanf("%d", &n);
    double ans = 0, tmp = 0, now;
    while(n --)
    {
        scanf("%lf", &tmp);
        now *= tmp;
        ans += 2 * now + tmp;
        now += tmp;
    }
    printf("%.10f\n", ans);
}



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
区间DP是一种动态规划的方法,用于解决区间范围内的问题。在Codeforces竞赛中,区间DP经常被用于解决一些复杂的字符串或序列相关的问题。 在区间DP中,dp[i][j]表示第一个序列前i个元素和第二个序列前j个元素的最优解。具体的转移方程会根据具体的问题而变化,但是通常会涉及到比较两个序列的元素是否相等,然后根据不同的情况进行状态转移。 对于区间长度为1的情况,可以先进行初始化,然后再通过枚举区间长度和区间左端点,计算出dp[i][j]的值。 以下是一个示例代码,展示了如何使用区间DP来解决一个字符串匹配的问题: #include <cstdio> #include <cstring> #include <string> #include <iostream> #include <algorithm> using namespace std; const int maxn=510; const int inf=0x3f3f3f3f; int n,dp[maxn][maxn]; char s[maxn]; int main() { scanf("%d", &n); scanf("%s", s + 1); for(int i = 1; i <= n; i++) dp[i][i] = 1; for(int i = 1; i <= n; i++) { if(s[i] == s[i - 1]) dp[i][i - 1] = 1; else dp[i][i - 1] = 2; } for(int len = 3; len <= n; len++) { int r; for(int l = 1; l + len - 1 <= n; l++) { r = l + len - 1; dp[l][r] = inf; if(s[l] == s[r]) dp[l][r] = min(dp[l + 1][r], dp[l][r - 1]); else { for(int k = l; k <= r; k++) { dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r]); } } } } printf("%d\n", dp[n]); return 0; } 希望这个例子能帮助你理解区间DP的基本思想和应用方法。如果你还有其他问题,请随时提问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值