1. 程式人生 > 其它 >Solution -「Hdu3037」Saving Beans

Solution -「Hdu3037」Saving Beans

Prob.

給定 \(m\) 個相同球,\(n\) 個不同的盒子。

求在這 \(n\) 個盒子中放不超過 \(m\) 個球的方案數,並對 \(p\) 取模。

其中 \(1 \leq n, m \leq 10^9, 1 < p < 10^6\),且 \(p\) 為質數。


Sol.

考慮先大力寫出原題對應的組合數的柿子。

那就先來看一個經典模型:求 \(x\) 個不同盒子中放 \(y\) 個球允許盒子空著的方案數。

我們可以運用插板法。總共 \(y - 1\) 個空位,插入 \(x - 1\) 塊板,這是不允許空著的方案。

我們可以嘗試再給它額外分配 \(x\) 個空位,只要插到了這些空位裡,就相當於當前分出的盒子取空。故該模型的答案即為 \(\dbinom {x + y - 1} {x - 1}\)

不難發現原題就是在這個模型上又套了一個求和,即 \(x\) 固定但 \(y\) 不定。那麼原題的答案即為 \(\large{\sum \limits_ {i = 0} ^{m}} \dbinom {i + n - 1} {n - 1}\)

恆等變換一下,即 \(\large{\sum \limits_ {i = 0} ^{m + n - 1}} \dbinom {i} {n - 1}\)

再由利用變上項求和公式可得 \(\large{\sum \limits_ {i = 0} ^{m + n - 1}} \dbinom {i} {n - 1} = \dbinom {m + n} {n}\)

現在我們就將其化為了簡潔的一個組合數的形式,瓶頸在於 \(m, n\) 過大。

關於大數組合數的取模不難想到 Lucas ((。


Code.

#include <cstdio>

typedef long long LL;
int Abs(int x) { return x < 0 ? -x : x; }
int Max(int x, int y) { return x > y ? x : y; }
int Min(int x, int y) { return x < y ? x : y; }

int read() {
    int x = 0, k = 1;
    char s = getchar();
    while(s < '0' || s > '9') {
        if(s == '-')
            k = -1;
        s = getchar();
    } 
    while('0' <= s && s <= '9') {
        x = (x << 3) + (x << 1) + (s ^ 48);
        s = getchar();
    }
    return x * k;
}

void write(int x) {
    if(x < 0) {
        x = -x;
        putchar('-');
    }
    if(x > 9)
        write(x / 10);
    putchar(x % 10 + '0');
}

void print(int x, char s) {
    write(x);
    putchar(s);
}

const int MAXN = 1e5 + 5;

int fac[MAXN], inv[MAXN], mod;

int Quick_Pow(int a, int b) {
    int res = 1;
    while(b) {
        if(b & 1)
            res = (LL)res * a % mod;
        a = (LL)a * a % mod;
        b >>= 1;
    }
    return res;
}

int C(int n, int m) {
    int res = 1;
    for(int i = n - m + 1; i <= n; i++)
        res = (LL)res * i % mod;
    int down = 1;
    for(int i = 2; i <= m; i++)
        down = (LL)down * i % mod;
    return (LL)res * Quick_Pow(down, mod - 2) % mod;
}

int Lucas(int n, int m) {
    if(m < mod)
        return C(n, m);  
    return (LL)Lucas(n / mod, m / mod) * C(n % mod, m % mod) % mod;
}

int main() {
    int t = read();
    while(t--) {
        int n = read(), m = read();
        mod = read();
        print(Lucas(m + n, n), '\n');
    }
    return 0;
}