CF 618G

題目大意

一開始有N 個空位。給定一個概率P 。每次你有P 的概率在第N 個空位加入一個1 ,有1P 的概率加入一個2 。加入之後,你會將這個數字往前推,假如當前數字遇到了另外一個和他值一樣的數字,設爲v ,那麼當前空位的數字變爲v+1 ,並且繼續往前推。直到到達邊界或遇到一個不一樣的數字。遊戲結束當且僅當所有空位都放了數字。一種結束局面的權值定義爲所有數字的和。問你最終結束時局面的期望權值。

數據範圍

N109
109P1 ,且P 的精度範圍爲109

題解

Ai,j 表示當前有i 個格子,能夠湊出數字j 的概率,注意只能能湊出,沒有保證最終一定是。那麼很顯然的,Ai,j=Ai,j1Ai1,j1

Bi,j 的意義是一樣的。只是保證在一開始放的數字是2。那麼
Bi,j=Bi,j1Ai1,j1

那麼我們再做些更正,即Ai,j,Bi,j 的意義變爲第i 個恰好爲j 的概率。那麼
Ai,j=Ai,j(1Ai1,j)A 是上面求的A

而且可以發現的一點是,當j 很大時,Ai,j 就趨近於0了。因爲他大概是以平方級別變化,那麼事實上當j50 時就等於0了。
還有一點是,當i 比較大時,Ai,j=Ai1,j ,分析方法同理。那麼事實上,只需要預處理出1i50 的值就好了。

接來下考慮怎麼求答案。

dpi,j 表示當前有i 個格子,第一個格子的值爲j ,且不會與其他數字結合的期望權值。我們對j 進行分類討論。

  1. j=1
    那麼只需要第二個格子不是1就好了。可以得到遞推式
    dpi,j=j+50k=2dpi1,kBi1,k50k=2Bi1,k

這就是爲什麼我們要預處理出B ,因爲我們第一個塞進來的不能是1,否則就合併了。
而且遞推式的分母其實就是出現這種情況的概率。

2.j>1
可以發現的是,第二個格子的值必然比j 要小,不然的話肯定會發生合併。那麼我們就可以得到另一條式子了

dpi,j=j+j1k=1dpi1,kAi1,kj1k=1Ai1,k

但這題到這裏還沒結束,因爲N109 !
但之前說了,由於j50 而且當N>50AN,j=AN1,j ,也就是說,我們只需要求出dp1>50 ,對於N>50 的,每次轉移的係數都是一樣的!那麼我們就可以構出轉移的矩陣,直接轉移就好了。

那麼我們最終的答案就是

j=150dpN,jAN,j

代碼:

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int MAXN = 55;

typedef double Matrix[MAXN][MAXN];

Matrix A,B,Trans,Dp,Cur;
double Out[55];
int N;

void Mul(Matrix &a,Matrix &b,Matrix &c)
{
    static Matrix Tmp;
    memset(Tmp,0,sizeof Tmp);
    for(int i = 0;i <= 50;i ++)
        for(int k = 0;k <= 50;k ++)
            for(int j = 0;j <= 50;j ++)
                Tmp[i][j] += a[i][k] * b[k][j];
    memcpy(c,Tmp,sizeof c);
}

int main()
{
//  freopen("data.in","r",stdin),freopen("data.out","w",stdout);
    double p,q;
    scanf("%d%lf", &N, &p);
    p = p / (1e9),q = 1 - p;
    for(int i = 1;i <= 50;i ++)
        for(int j = 1;j <= 50;j ++)
        {
            if (j == 1) A[i][j] += p;
            if (j == 2) A[i][j] += q,B[i][j] += q;
            A[i][j] += A[i][j - 1] * A[i - 1][j - 1];
            B[i][j] += B[i][j - 1] * A[i - 1][j - 1];
        }
    for(int i = 50;i;i --)
        for(int j = 1;j <= 50;j ++)
            A[i][j] *= (1 - A[i - 1][j]),B[i][j] *= (1 - A[i - 1][j]);
    for(int j = 1;j <= 50;j ++) Dp[1][j] = j;
    for(int i = 2;i <= 50;i ++)
        for(int j = 1;j <= 50;j ++)
        {
            double s = 0;
            if (j == 1)
            {
                for(int k = 2;k <= 50;k ++) Dp[i][j] += Dp[i - 1][k] * B[i - 1][k],s += B[i - 1][k];
            } else
                for(int k = 1;k < j;k ++) Dp[i][j] += Dp[i - 1][k] * A[i - 1][k],s += A[i - 1][k];
            Dp[i][j] /= s;
            Dp[i][j] += j;
        }
    Trans[0][0] = 1;
    for(int j = 1;j <= 50;j ++) Trans[0][j] = j;
    //For j = 1
    double s = 0;
    for(int k = 2;k <= 50;k ++) s += B[50][k];
    for(int k = 2;k <= 50;k ++) Trans[k][1] += B[50][k] / s;
    //For j not equal 1
    for(int j = 2;j <= 50;j ++)
    {
        s = 0;
        for(int k = 1;k < j;k ++) s += A[50][k];
        for(int k = 1;k < j;k ++) Trans[k][j] += A[50][k] / s;
    }   
    int bk = N;
    if (N > 50)
    {
        memcpy(Cur[0],Dp[50],sizeof Cur);
        Cur[0][0] = 1;
        for(N -= 50;N;N >>= 1)
        {
            if (N & 1) Mul(Cur,Trans,Cur);
            Mul(Trans,Trans,Trans);
        }
        memcpy(Out,Cur[0],sizeof Out);
    } else memcpy(Out,Dp[N],sizeof Out);
    double ans = 0;
    for(int j = 1;j <= 50;j ++) ans += Out[j] * A[bk > 50 ? 50 : bk][j];
    printf("%.15f\n", ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章