CW高中-C0486A

Problem

给定 \(n\) 种牌,每种牌抽出的概率相同。集齐所有种类的卡牌时停手。

设有 \(k\) 种牌恰好只被抽中过一次,求 \(k\) 的幂次的期望(不是期望的幂次)。

即求出 \(E[k],E[k^2],\cdots,E[k^m]\),对输入的质数 \(P\) 取余。

\(n\le 3\times 10^5,m\le 200,9\times 10^8<P\le10^9+7\)

Solution

\(A_i\) 表示 \(i \text{ 恰好出现一次}\) 这样一个事件,\(I_i=[A_i]\),其中 \([]\) 是艾佛森括号,显然 \(E(I_i)=P(A_i)\)

\(X\) 表示恰好只被抽中一次的牌数

考虑平方的组合意义,就是选了 \((i,j)\) 的有序对,使得第 \(i,j\) 种牌都恰好出现一次,这样对数的期望个数就是平方的意义。

因此有:

\[X^2=\sum_{i=1}^n\sum_{j=1}^nI_iI_j \\ \begin{aligned} E(X^2)&=\sum_{i=1}^n\sum_{j=1}^nP(A_iA_j) \\ &=2\sum_{i=1}^n\sum_{j=i+1}^nP(A_iA_j)+\sum_{i=1}^nP(A_i)\\ &=2E(\tbinom{X}{2})+E(X) \end{aligned} \]

不难发现:

\[E(X^m)=\sum_{1\le i_1,i_2\cdots, i_m\le n}P(A_{i_1}A_{i_2}\cdots A_{i_m}) \\ E(\tbinom{X}{k})=\sum_{i_1<i_2<\cdots<i_k}P(A_{i_1}A_{i_2}\cdots A_{i_k}) \\ E(X^m)=\sum_{k=0}^m k!S(m, k)E(\tbinom{X}{k}) \]

其中 \(S(m,k)\) 是第二类斯特林数,这是基本的组合学知识。

\(P(A_{i_1}\cdots A_{i_k})\) 可以通过递推得到。值得一提的是,因为递推中的转移可能会指向自身,因此需要用到扰动甚至有限微积分求出一个系数。具体可以设计一个函数 \(f(i,j)\) 表示已抽出 \(i\) 张牌,\(j\) 张恰好抽出了一次。转移是简单的。

另外,值得一提的是当 \(m=1\) 时的一种巧妙做法。

考虑按第一次抽出某种牌的前后顺序来顺次考虑,会发现这样显然期望是不变的。

那么考虑第 \(i\) 种牌仅抽出一次的概率是多少。考虑第 \(i\) 种牌仅抽出一次的方案集合,考虑把抽出这张牌的抽卡轮次与抽出最后一张牌的抽卡轮次交换,方案构成了双射,因此他作为最后一张牌被抽出的概率和这种牌只被抽出过一次的概率应该是相同的,都为 \(\frac{1}{n-i+1}\)

因此 \(m=1\) 时答案为 \(\sum_{i=1}^n\frac{1}{i}\)

Code

时间很极限所有多数人使用 \(\text{Barrett}\) 处理计算,这份代码是卡常过的,最好还是写写 \(\text{Barrett}\)

// Not afraid to dark.

#include <bits/stdc++.h>
using namespace std;

#define int unsigned int

const int Nv = 3e5, Mv = 2e2;
int n, m, mod;
int inv[Nv + 5];
int C[Nv + 5][Mv + 5], S[Mv + 5][Mv + 5], ans[Mv + 5];

signed main (){
    scanf ("%u%u%u", &n, &m, &mod);
    inv[1] = 1;
    for (int i = 2;i <= max (n, m);++ i)
        inv[i] = 1ull * inv[i - mod % i] * (mod / i + 1) % mod;
    C[0][0] = 1;
    unsigned long long kd = 1;
    for (int i = 0;i < n;++ i){
        for (int j = min (m, i);;-- j){
            if (C[i][j] >= mod)
                C[i][j] -= mod;
            unsigned long long p = 1ull * C[i][j] * inv[n - i + j] % mod;
            C[i + 1][j] += p;
            C[i + 1][j + 1] += p;
            if (! j)
                break;
        }
        kd = kd * (n - i) % mod;
    }
    for (int i = 0;i <= m;++ i)
        C[n][i] = C[n][i] * kd % mod;
    S[0][0] = 1;
    for (int i = 1;i <= m;++ i)
        for (int j = 1;j <= i;++ j)
            S[i][j] = (S[i - 1][j - 1] + 1ull * S[i - 1][j] * j % mod) % mod;
    for (int i = 1;i <= m;++ i){
        for (int j = 0, k = 1;j <= i;k = 1ull * k * (++ j) % mod)
            ans[i] = (ans[i] + 1ull * k * S[i][j] % mod * C[n][j] % mod) % mod;
        printf ("%u\n", ans[i]);
    }
    return 0;
}
posted @ 2024-02-20 20:31  Imcaigou  阅读(17)  评论(2编辑  收藏  举报