Solution -「Hdu3037」Saving Beans
阿新 • • 發佈:2021-11-27
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; }