洛谷 P6944 [ICPC2018 WF]Gem Island 题解

一、题目:

洛谷原题

codeforces原题

二、思路:

一道计数题。之所以不说它是一道期望题,是因为这道题的难点并不在期望,而在于计数。

\(d\)天过后,每个人手中的宝石数为\((g_1,g_2,g_3,\ldots,g_n)\),则每种可能的\(\{g_n\}\)的概率是相同的,均为\(\dfrac{1}{\dbinom {n+d-1}{d}}\)。这就对应着那个基本结论:不定方程\(x_1+x_2+\ldots+x_n=n+d\)解的种数为\(\dbinom{n+d-1}{n-1}\),其中\(x_i\)为正整数。证明可以用高中数学的隔板法。

那么这是为什么呢?官方题解是这样证明的,对\(d\)使用数学归纳法。

  1. \(d=0\)时,只有一种可能的分配方法,即\((1,1,\ldots,1)\)。显然成立。

  2. 假设当\(d=k(k\in \N)\)时,猜想成立,即每种可能的分配方法的概率均相同,设为\(p\)

    则当\(d=k+1\)时,考虑对于任意的一种分配方法\((g_1,g_2,\ldots,g_n)\),它一定是由某个状态\((g_1,g_2,\ldots,g_k-1,\ldots,g_n)\)转移而来,因此,\((g_1,g_2,\ldots,g_n)\)的概率就是\(\sum_k p\times \dfrac{g_k-1}{n+d-1}\),即\(\dfrac{p}{n+d-1}\sum_k{(g_k-1)}\),即\(\dfrac{p\times d}{n+d-1}\),猜想也成立。

综合1、2,\(\forall d\in \N\),每种分配方法的概率均相同。


既然每种方法的概率相同,所以我们只需要关心满足条件的所有的\({g_n}\)中前\(r\)大的数之和,即为\(S(n,d)\),考虑怎样能递推出\(S(n,d)\)来。

\(n\leq r\)时,\(S(n,d)=\dbinom{n+d-1}{d}\times(n+d)\)

\(n>r\)时,将\(g_1,g_2,\ldots,g_n\)的贡献分成两部分,一部分是每个人的第一个宝石对答案的贡献,为\(r\dbinom{n+d-1}{d}\),另一部分是每个人剩下的宝石对答案的贡献,为\(\sum\limits_{i=n-d}^{n-1}\dbinom{n}{i}\times S(n-i,d-(n-i))\)(考虑枚举有\(i\)个人原本就只有一个宝石)。

所以\(S(n,d)=r\times \dbinom{n+d-1}{d}+\sum\limits_{i=n-d}^{n-1}\dbinom{n}{i}\times S(n-i,d-(n-i))\)

时间复杂度:\(O(n^2d)\)

三、代码:

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

inline int read(void) {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return f * x;
}

const int maxn = 505;

int N, D, r;

double C[maxn << 1][maxn], S[maxn][maxn];

void init(void) {
    C[0][0] = 1;
    for (int i = 1; i <= N + D; ++ i) {
        C[i][0] = 1;
        for (int j = 1; j <= max(N, D); ++ j) {
            C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
        }
    }
}

int main() {
    N = read(); D = read(); r = read();
    init();
    for (int n = 1; n <= r; ++ n) {
        for (int d = 0; d <= D; ++ d) {
            S[n][d] = (n + d) * C[n + d - 1][d];
        }
    }
    for (int n = r + 1; n <= N; ++ n) {
        for (int d = 0; d <= D; ++ d) {
            for (int g = n - d; g <= n - 1; ++ g) {
                S[n][d] += C[n][g] * S[n - g][d - (n - g)];
            }
            S[n][d] += C[n + d - 1][d] * r;
        }
    }
    printf("%.10lf\n", S[N][D] / C[N + D - 1][D]);
    return 0;
}
posted @ 2021-05-09 20:57  蓝田日暖玉生烟  阅读(125)  评论(0编辑  收藏  举报