【hdu3037】Saving Beans——组合数取模
题目的大意是求在n棵树上采摘不超过m颗豆子的方案数,要求答案对给定的p取模,同时保证p为质数。
奉上大神关于这道题的公式的推导及变形:戳这里
最后就是求C(n+m,m)%p啦~
这里因为p<=1e5,而n和m又很大,所以应该要用到Lucas定理:
Lucas(n,m,p)=C(n%p,m%p)*Lucas(n/p,m/p,p),证明有兴趣的自行百度。
注意要预处理阶乘才不会TLE,因为p是要求读入的所以每组数据都要预处理一次。
由费马小定理可知b关于p(要求p是质数)的逆元是b^(p-2),所以这里还要写一个快速幂取模求逆元。
代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 typedef long long LL; 5 using namespace std; 6 LL n,m,p; 7 LL jie[100005]; 8 LL read() 9 { 10 LL ans=0,f=1;char c=getchar(); 11 while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} 12 while(c>='0'&&c<='9'){ans=ans*10+c-48;c=getchar();} 13 return ans*f; 14 } 15 LL kuai(LL a,LL b) 16 { 17 LL res=1; 18 while(b){ 19 if(b&1)res=res*a%p; 20 a=(a*a)%p; 21 b>>=1; 22 } 23 return res; 24 } 25 LL C(LL x,LL y) 26 { 27 if(x<y)return 0; 28 LL ans; 29 ans=jie[x]*kuai(jie[y],p-2)%p*kuai(jie[x-y],p-2)%p; 30 return ans; 31 } 32 LL lucas(LL x,LL y) 33 { 34 LL ans=1; 35 while(x&&y&&ans){ 36 if(x%p<y%p)return 0; 37 ans=(ans*C(x%p,y%p))%p; 38 x/=p; 39 y/=p; 40 } 41 return ans; 42 } 43 int main() 44 { 45 LL t; 46 t=read(); 47 while(t--){ 48 n=read();m=read();p=read(); 49 jie[0]=jie[1]=1; 50 for(int i=2;i<=p;i++)jie[i]=jie[i-1]*i%p; 51 printf("%lld\n",lucas(n+m,m)); 52 } 53 return 0; 54 }