The 2021 CCPC Weihai Onsite - M. 810975 題解


title: The 2021 CCPC Weihai Onsite - M. 810975 題解
date: 2021-11-25 19:04:21
tags: [inclusion-exclusion, combinatorics, math, FFT, team training]
mathjax: true

題意

Zayin進行了 \(n\) 場比賽, 獲勝 \(m\) 局,其中最長連勝是 \(k\) ,問有多少種情況

思路

問題轉化

考慮敗場分割勝場(這是一個經典解法:隔板法),問題轉化為

\[\sum _{i=1} ^{n-m+1} {x_i} = m \ (x_i \leq k) \]

的非負整數解個數(整數拆分問題)。

\[\begin{aligned} G(x) &= \Bigg( \sum _{i=0} ^{k} x^i \Bigg) ^ {n-m+1} \\ &= (1+x+x^2+x^3+ \cdots +x^k) ^ {n-m+1} \\ &= \Bigg(\frac{1}{1-x} - \frac{x^{k+1}}{1-x} \Bigg) ^ {n-m+1} \end{aligned} \]

TIP: 如果你打算跑NTT快速冪 / 生成函數展開,看到這里就結束了。

引理

\[\sum _{i=1} ^{p} {x_i} = m \]

的非負整數解個數為

\[C(m+p-1,\ p-1) = C ^{p-1} _{m+p-1} \]

引理證明

先考慮正整數解個數

考慮有 \(m\) 個球,分為 \(p\) 組,那么只需要這么考慮

O _ O _ O _ O _ O _ ....... _ O _ O // O 表示球, _ 表示空位

那么往空位塞入 \(p-1\) 個隔板即可,\(C(m-1,\ p-1)\)

再考慮非負整數解個數

\(y_i = x_i + 1\)

也就是考慮:先給每組球多塞一個球,作上述隔板法,就可以擴展到非負整數了

\[\begin{aligned} &\sum _{i=1} ^{p} x_i &= &\ \ m \\ &\sum _{i=1} ^{p} (x_i + 1) &= &\ \ m + p \\ &\sum _{i=1} ^{p} {y_i} &= &\ \ m + p \end{aligned} \]

因為第三個式子正整數解個數即為 \(C(m+p-1,\ p-1)\),也等於第一個式子的非負整數解個數

至此,我們得到了 \(n\) 場比賽,獲勝 \(m\) 場的情況數

回到原問題

以和引理一樣的思路,我們考慮某一場比賽 至少 贏了 \(k+1\) 次(因為組合的性質,我們不好解決恰好 \(k\) 次的問題),減去至少贏了 \(k\) 次就是答案了。

為了防止問題太過抽象,我們考慮 \(n=7,m=6,k=3,p=n-m+1=2\) 的情況,顯然只有一種

首先,我們先抽一個 \(k\) 出來,算 \(\sum _{i=1} ^{p} {y_i} = y_1+y_2 = m - k = 3\) 情況數,然后 \(C(p,1)\) 塞給某1個 \(y_i\) ,即可保證得到的組合至少存在1個大於 \(k\) 的數。通過引理我們知道答案是 \(C(3+2-1, 2-1)\cdot C(2,1) = C(4,1) \cdot 2 = 8\)

同理,\(k+1\)也是這么算,得到答案 \(6\) ,但是 \(8 - 6 = 2 \not = 1\)

通過式子所代表的含義,我們列舉出生成的 \(8\)\(k\) 的組合和 \(6\)\(k+1\) 的組合。(如果讀者在這里沒發現這里需要容斥,還請模擬一下8+6種情況,不會花太多時間)

發現不對,\(y_1 = y_2 = 3\) 被計入了 \(2\) 次。顯然,這是因為+3計數重復。本質上,我們要求的是 y[1] >= 3 or y[2] >= 3 (記作 \(A\) ) ,但是我們這里算的是的是y[1] >= 3 + y[2] >= 3 (記作 \(B\) ),要想不重復,我們只能減去y[1] >= 3 and y[2] >= 3(記作 \(C\) )。即 \(A = B - C\),同理,我們可以推廣到 p > 2 的情況,即計算

\[Ans|_{k} = \sum _{i=1} ^{\infty} (-1)^i \cdot C(n - m + 1, i) \cdot C(m - i \cdot k + n - m , n - m) \]

\(i\) 表示枚舉有 \(i\) 個元素 \(\geq k\),左邊那個組合數表示選 \(i\) 個數 \(+k\) ,右邊那個組合數即為整數拆分非負整數解個數

\(k+1\) 同理。

最后

\[Answer = Ans|_k - Ans|_{k+1} \]

小細節

具體看代碼特判

代碼

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

const i64 MOD = 998244353;
const int N = 1e5 + 6;
i64 fac[N], inv[N], ifac[N];

void norm(i64 &x) {
    while(x>=MOD) x -= MOD;
    while(x<0) x += MOD;
}

i64 C(i64 n,i64 m) {
    if(m<0 || n<m) return 0;
    return fac[n] * ifac[m] % MOD * ifac[n-m] % MOD;
}

int main(int argc, char const *argv[])
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr); cout.tie(nullptr);

    // freopen("in.txt","r",stdin);

    fac[0] = fac[1] = 1;
    inv[0] = inv[1] = 1;
    ifac[0] = ifac[1] = 1;

    for(int i=2;i<N;++i) {
        fac[i] = fac[i-1] * i % MOD;
        inv[i] = inv[MOD%i] * (MOD-MOD/i) % MOD;
        ifac[i] = ifac[i-1] * inv[i] % MOD;
    }

    i64 n,m,k;
    cin >> n >> m >> k;

    if(!(n>=m && m>=k)) {
        cout << 0;
        return 0;
    }
    
    if(k==0) {
        if(m==0) cout << 1;
        else cout << 0;
        return 0;
    }

    vector<i64> a(m+2), b(m+2);
    i64 w = m, f = n - m;

    i64 ans = 0;
    for(int i=1;i*k<=m;++i) {
        // k
        a[i] = C(f + 1, i) * C(m - i * k + f, f) % MOD;
        // k + 1
        b[i] = C(f + 1, i) * C(m - i * (k + 1) + f, f) % MOD;

        ans += (i&1) ? (a[i] - b[i]) : (b[i] - a[i]);
        norm(ans);
    }

    cout << ans;

    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM