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;
}
posted @ 2021-11-26 22:05  STrAduts  阅读(40)  评论(1)    收藏  举报