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

_TCgogogo_發表於2015-08-25


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);
}



相關文章