1. 程式人生 > >CodeForces 1096E: The Top Scorer

CodeForces 1096E: The Top Scorer

一道經典組合數學+容斥題。

題意簡述:

\(p\) 個人,每個人有得分 \(a_i\)
總得分 \(\sum a_i = s\)
第一個人得分 \(a_1 \ge r\)

得分最高的人可以獲勝,如果多個人得分最高,則等概率隨機其中一個人獲勝。

問第一個人獲勝的概率。

題解:

第一個人要獲勝,他的得分必須是最高分。

考慮列舉第一個人的得分,假設 \(a_1 = x\)

再列舉總共有幾個人得分和第一個人一樣(包括第一個人),假設有 \(i\) 個。

那麼剩下的 \(p - i\) 個人的得分必須滿足 \(a_i < x\)\(\sum a_i = s - ix\)

也就是說,\(s - ix\) 個無標號小球,放進 \(p - i\) 個有標號盒子中,允許空盒子,每個盒子最多放 \(x - 1\) 個球。

經典的組合問題:\(n\) 個小球放入 \(m\) 個盒子,允許空盒子的方案數為 \(\binom{n + m - 1}{m - 1}\)

加上了球數不能大於等於 \(x\) 的限制,考慮容斥:

  • 列舉超過限制的盒子數 \(0 \le i \le m\),容斥係數是 \((-1)^i\binom{m}{i}\)

  • \(i\) 個盒子超過了限制的答案是 \(\binom{n-ix+m-i-1}{m-i-1}\)

所以答案是 \(\sum\limits_{i=0}^{m}(-1)^i\binom{m}{i}\binom{n-ix+m-i-1}{m-i-1}\)

總答案是 \(\sum\limits_{x=r}^{s}\sum\limits_{i=1}^{p}\binom{p}{i}\left(\sum\limits_{j=0}^{p-i}(-1)^j\binom{p-i}{j}\binom{n-ix-jx+p-i-j-1}{p-i-j-1}\right)/i\)
當然這式子太長了,不如封裝一下。

注意各種組合數無意義的情況,continue掉就好了。

複雜度 \(O(p^2s)\)

#include <cstdio>

typedef long long LL;
const int Mod = 998244353;

inline int chk(int x) { if (x < 0) x += Mod; if (x >= Mod) x -= Mod; return x; }

inline int qPow(int b, int e)
{
    int a = 1;
    for (; e; b = (LL)b * b % Mod, e >>= 1)
        if (e & 1) a = (LL)a * b % Mod;
    return a;
}

int p, s, r, c[5105][105];

void Init()
{
    for (int i = 0; i <= 5100; ++i) c[i][0] = 1;
    for (int i = 0; i <= 5100; ++i)
        for (int j = 1; j <= i && j <= 100; ++j)
            c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % Mod;
}

inline int Calc(int n, int m, int x)
{
    LL S = 0;
    for (int i = 0; i <= m && i * x <= n; ++i)
    {
        LL s = (LL)c[m][i] * c[n - x * i + m - 1][m - 1] % Mod;
        S += i & 1 ? -s : s;
    }
    return (S % Mod + Mod) % Mod;
}

int Sum1, Sum2;

int main()
{
    scanf("%d%d%d", &p, &s, &r);
    if (p == 1) return puts("1"), 0;
    Init();
    for (int x = r; x <= s; ++x)
    {
        if (x * p < s) continue;
        for (int i = 1; i <= p; ++i)
        {
            if (i * x > s || (p - i) * (x - 1) + i * x < s)
                continue;
            if (i == p) { Sum2 = (Sum2 + (i * x == s ? qPow(i, Mod - 2) : 0)) % Mod; continue; }
            Sum2 = (Sum2 + (LL)c[p - 1][i - 1] * Calc(s - i * x, p - i, x) % Mod * qPow(i, Mod - 2)) % Mod;
        }
    }
    Sum1 = c[s - r + p - 1][p - 1];
    printf("%lld\n", (LL)Sum2 * qPow(Sum1, Mod - 2) % Mod);
    return 0;
}

式子也可以寫成 \(p!\sum\limits_{x=r}^{s}\sum\limits_{i=1}^{p}\frac{1}{(m-i)!}\binom{n-ix-i-1}{p-i-1}\sum\limits_{j=1}^{i}\frac{(-1)^{i-j}}{(i-j)!}\cdot\frac{1}{j!\cdot j}\)。(驚了,可以卷積)

複雜度可以優化到 \(O(p\log p+sp)\)。(為什麼要寫