Lucas定理:
- 公式:C(n,m)%p=C(n/p,m/p)*C(n%p,m%p)%p
- 适用范围:n和m很大(1e18数量级),mod很小(1e6数量级)
- 对于上式:C(n%p,m%p)可直接用逆元求组合数方法求,C(n/p,m/p)继续用lucas定理。
- 代码:
ll Lucas(int n,int m){ if(n < m) return 0; else if(m == 0) return 1; else return Comb(n%mod,m%mod) * Lucas(n/mod,m/mod) % mod; }
HDU3037
题意:
- 共有m个豆放在n棵不同的树上,可以全部放、或部分放、或者不放,求有多少种方法。
题解:
- 问题转化为把≤m的数字分成n部分,每一部分可以为0;
- 转化为把m+n个数分成n部分,每一部分不能为0。
- 把x分成n部分,每一部分不能为0的分发为,C(x+n-1,n-1)
- ans = ΣC(x+n-1,n-1)(x from 0 to m) = C(n+m,n)
- 因为n和m很大,所以适合用Lusca定理。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int const N = 100000 + 10;
int n,m,mod;
ll f[N];
void Init(){
f[0] = 1;
for(int i=1;i<=mod;i++)
f[i] = f[i-1] * i % mod;
}
ll pow_mod(ll a,int n,int mod){
ll ans = 1;
while(n){
if(n&1) ans = ans * a % mod;
a = a * a % mod;
n >>= 1;
}
return ans;
}
ll Comb(int n,int m){
if(n < m) return 0;
return f[n]*pow_mod((f[m]*f[n-m])%mod,mod-2,mod)%mod;
}
ll Lucas(int n,int m){
if(n < m) return 0;
else if(m == 0) return 1;
else return Comb(n%mod,m%mod) * Lucas(n/mod,m/mod) % mod;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
cin>>n>>m>>mod;
Init();
printf("%lld\n",Lucas(n+m,n));
}
return 0;
}