传送门
这题是求 0~m个果子放在不同的 n 棵树上有多少种放置方法
首先看 m 个果子的情况,用隔板法来处理这个情况。最多有 n-1 棵树能空着,那就加 n-1 个气泡进去。然后选择 n-1 个位置就把 m+n-1 分成了 n 段(n 棵树的情况)。这个对应的选择方式就是有 C(n+m-1, n-1),其实也就是 C(n+m-1, m)
那么果子有 0~m 种,所有总和就是
ΣC(n+i-1,i) (i 从 0 到 m)
我看展开这个公式看一眼
C(n-1,0)+C(n,1)+C(n+1,2)+C(n+2,3)+…+C(n+m-1,m)
我们回忆曾经学过的组合数递归公式
C(n,m)=C(n-1,m)+C(n-1,m-1)
由于 C(n-1,0) = C(n,0) = 1 ,那么我们把上面式子的第一项换成 C(n,0)
整个式子就变成了
C(n,0)+C(n,1)+C(n+1,2)+C(n+2,3)+…+C(n+m-1,m)
前两项可以结合成 C(n+1,1) ,整个式子就成了
C(n+1,1)+C(n+1,2)+C(n+2,3)+…+C(n+m-1,m)
又可以继续合并,合并到最后结果就是
C(n+m,m)
这时候我们发现 n 和 m 非常的大,相对来说 p 比较小
那我们就可以用 卢卡斯定理模板
#include
using namespace std;
using ll = long long;
ll n,m,p;
ll quick_mod(ll a, ll b, ll m) {
ll ans = 1;
while (b) {
if (b & 1) {
ans = ans * a % m;
}
a = a * a % m;
b >>= 1;
}
return ans;
}
ll comp(ll a, ll b, ll m) {
if (a < b) {
return 0;
}
if (a == b) {
return 1;
}
if (b > a - b) {
b = a - b;
}
ll ans = 1, ca = 1, cb = 1;
for (int i = 0; i < b; i++) {
ca = ca * (a - i) % m;
cb = cb * (b - i) % m;
}
ans = ca * quick_mod(cb, m - 2, m) % m;
return ans;
}
ll lucas(ll a, ll b, ll m) {
ll ans = 1;
while (a && b) {
ans = (ans * comp(a % m, b % m, m)) % m;
a /= m;
b /= m;
}
return ans;
}
int main() {
std::ios::sync_with_stdio(false);
int T;
cin >> T;
long long n, p, m;
while (T--) {
cin >> n >> m >> p;
cout << lucas(n+m, n, p) << endl;
}
return 0;
}