1. 程式人生 > 實用技巧 >BZOJ-4318 OSU!(概率dp)

BZOJ-4318 OSU!(概率dp)

題目描述

  一共有 \(n(n\leq 10^5)\) 次操作,每次操作只有成功與失敗之分,成功對應 \(1\),失敗對應 \(0\)\(n\) 次操作對應為一個長度為 \(n\)\(01\) 串。在這個串中連續的 \(x\)\(1\) 可以貢獻 \(x^3\) 的分數,這 \(x\)\(1\) 不能被其他連續的 \(1\) 所包含(也就是極長的一串 \(1\))。

  現在給出 \(n\),以及每個操作的成功率,輸出期望分數(四捨五入後保留 \(1\) 位小數)。

分析

  設 \(dp[i]\) 為以第 \(i\) 位為結尾的連續 \(1\) 的期望長度,則第 \(i\) 位的期望長度有 \(p[i]\)

的概率為 \(dp[i]=(dp[i-1]+1)\times p[i]\);有 \(1-p[i]\) 的概率為 \(dp[i]=0\)

  然後考慮計算每一位對答案的貢獻:\(E[(x+1)^3-x^3]=E[3x^2+3x+1]=3E(x^2)+3E(x)+1\),注意此處要維護長度的平方的期望,而不是期望長度的平方,因為 長度期望的平方不等於長度平方的期望,即 \(E(x^2)\neq E(x)^2\)。再用 \(dp2[i]\) 記錄以第 \(i\) 位為結尾的連續 \(1\) 的長度的平方的期望,可以藉助 \(dp[i]\) 來轉移,即 \(dp2[i]=(dp2[i-1]+2dp[i-1]+1)\times p[i]\)

程式碼

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
double dp[N],dp2[N],p[N];
char s[N];
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
        scanf("%lf",&p[i]);
    dp[0]=0;dp2[0]=0;
    double ans=0;
    for(int i=1;i<=n;i++)
    {
        dp[i]=(dp[i-1]+1)*p[i];
        dp2[i]=(dp2[i-1]+2*dp[i-1]+1)*p[i];
        ans=ans+(3*dp2[i-1]+3*dp[i-1]+1)*p[i];
    }
    printf("%.1lf\n",ans);
    return 0;
}