D - Dinner Bet(概率论 dp 数学推导)

http://codeforces.com/gym/101174/attachments

题意:

给两堆c个球in[1,n],每次随机选出d个求in[1,n],将两堆球中出现在这d个球的球删去,某堆空时结束,问期望次数。

解析:

显然,将球分为4种,A:第一堆独有,B:两堆共有,C:第二堆独有,D,其他

dp[I][J][K]表示ABC三堆分别已经有I,J,K个的期望步数。

考虑一步: d p [ I ] [ J ] [ K ] d p [ I + i ] [ J + j ] [ K + k ] dp[I][J][K]\to dp[I+i][J+j][K+k] 的概率

总方案数为 C n d C_n^d ,上面转移的方案数为
C A I i C B J j C C K k C n A B C + I + J + K d i j k C_{A-I}^i\cdot C_{B-J}^j\cdot C_{C-K}^k \cdot C_{n-A-B-C+I+J+K}^{d-i-j-k}

解释一下:前三个是填有用的位置的方案数,最后一个是填无用的方案数

两者之比就是状态转移的概率,我们设为 r a t e rate


麻烦的来了,一个状态可能转移到自己(概率设为 q q )。

根据题面,我们可以得出,这个状态转移的贡献为:(设目标状态期望步数为 x x

= r a t e ( 1 + x ) + r a t e q ( 2 + x ) + r a t e q 2 ( 3 + x ) . . . = r a t e ( 1 + 2 q + 3 q 2 + 4 q 3 + . . . + x + q x + q 2 x + . . . ) = r a t e ( ( q + q 2 + q 3 + . . ) + x ( 1 + q + q 2 + . . ) ) = r a t e ( ( q / ( 1 q ) ) + x ( 1 / ( 1 q ) ) ) = r a t e ( 1 / ( 1 q ) 2 + x / ( 1 q ) ) = rate*(1+x) + rate*q*(2+x) + rate*q^2*(3+x)...\\ = rate*(1+2q+3q^2+4q^3+... + x+qx+q^2x+... )\\ = rate*((q+q^2+q^3+..)' + x(1+q+q^2+..))\\ = rate*((q/(1-q))' + x(1/(1-q)))\\ = rate*(1/(1-q)^2 + x/(1-q))

中间有一步很难想,先求积分,利用 q 1 q\leq1 的性质转化后再求导。

代码:

/*
 *  Author : Jk_Chen
 *    Date : 2020-03-29-13.57.30
 */
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define rep(i,a,b) for(int i=(int)(a);i<=(int)(b);i++)
#define per(i,a,b) for(int i=(int)(a);i>=(int)(b);i--)
#define mmm(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define pill pair<int, int>
#define fi first
#define se second
void test(){cerr<<"\n";}
template<typename T,typename... Args>void test(T x,Args... args){cerr<<x<<" ";test(args...);}
const LL mod=1e9+7;
const int maxn=1e5+9;
const int inf=0x3f3f3f3f;
LL rd(){ LL ans=0; char last=' ',ch=getchar();
    while(!(ch>='0' && ch<='9'))last=ch,ch=getchar();
    while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
    if(last=='-')ans=-ans; return ans;
}
#define rd rd()
/*_________________________________________________________begin*/

int n,d,c;
int A,B,C;

LL CC[52][52];
const double eps=1e-6;
double dp[11][11][11];
double dfs(int I,int J,int K){// 已经有IJK的期望步数
//    test(I,J,K);
    if(dp[I][J][K]>=-eps)return dp[I][J][K];
    if(I+J==A+B||J+K==B+C)return dp[I][J][K]=0;
    double &D=dp[I][J][K];
    D=0;

    double q=0;   // 回到自己的概率
    rep(i,0,A-I)rep(j,0,B-J)rep(k,0,C-K){// 增加
        if(d-i-j-k>n-A-B-C+I+J+K)continue;
        if(i+j+k>d)break;
        double rate=1.0*CC[A-I][i]*CC[B-J][j]*CC[C-K][k]/CC[n][d]*CC[n-A-B-C+I+J+K][d-i-j-k];
        assert(rate>-eps&&rate<=1+eps);

        if(i+j+k==0)
            q=rate;
        else{
            double x=dfs(I+i,J+j,K+k);
            /*  = rate*(1+x) + rate*q*(2+x) + rate*q^2*(3+x)...
                = rate*(1+2q+3q^2+4q^3+...  +  x+qx+q^2x+... )
                = rate*((q+q^2+q^3+..)' + x(1+q+q^2+..))
                = rate*((q/(1-q))' + x(1/(1-q)))
                = rate*(1/(1-q)^2 + x/(1-q))
            */
            D+=rate*(1.0/(1-q)/(1-q) + x/(1-q));
        }
    }
    assert(D>-eps);
    return D;
}

int main(){
    rep(i,0,50)CC[i][0]=1;
    rep(i,1,50)rep(j,1,i){
        CC[i][j]=CC[i-1][j]+CC[i-1][j-1];
    }
    n=rd,d=rd,c=rd;
    bool vis[52];mmm(vis,0);
    A=0,B=0,C=0;
    rep(i,1,c){
        vis[rd]=1;
    }
    A=c;
    rep(i,1,c){
        int num=rd;
        if(vis[num])B++,A--;
        else C++;
    }
    mmm(dp,-1);
    printf("%.6f\n",dfs(0,0,0));
    return 0;
}

/*_________________________________________________________end*/

发布了773 篇原创文章 · 获赞 345 · 访问量 20万+

猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/105180815