hdu 3037 Lucas定理
题目可以转换成 x1+x2+……+xn=m 有多少组解,m在题中可以取0-m。
x1+x2+...+xn = m的解的个数,利用插板法可以得到方案数为:
(m+1)*(m+2)...(m+n-1) = C(m+n-1,n-1) = C(m+n-1,m)
现在就需要求不大于m的,相当于对i = 0,1...,m对C(n+i-1,i)求和,根据公式C(n,k) = C(n-1,k)+C(n-1,k-1)得
C(n-1,0)+C(n,1)+...+C(n+m-1,m)
= C(n,0)+C(n,1)+C(n+1,2)+...+C(n+m-1,m)
= C(n+m,m)
现在就是要求C(n+m,m) % p,其中p是素数。
然后利用Lucas定理的模板就可以轻松的求得C(n+m,m) % p的值
下面简单介绍一下Lucas定理:
Lucas定理是用来求 C(n,m) mod p的值,p是素数(从n取m组合,模上p)。
描述为:
Lucas(n,m,p)=C(n%p,m%p)* Lucas(n/p,m/p,p)
Lucas(x,0,p)=1;
求一个组合数最后可化简为(a/b)%p
(a/b)%p可以转换成a*Inv(b,p) Inv(b,p)为b对p的逆元。
逆元 :(b/a) (mod n) = (b * x) (mod n)。 x表示a的逆元。并且 a*x ≡ 1 (mod n ) 注意:只有当a与n互质的时候才存在逆元
Sample Input
2
1 2 5
2 1 5
Sample Output
3
3
1 # include <iostream> 2 # include <cstdio> 3 # include <cstring> 4 # include <algorithm> 5 # include <string> 6 # include <cmath> 7 # include <queue> 8 # define LL long long 9 using namespace std ; 10 11 LL n, m, p; 12 13 LL Ext_gcd(LL a,LL b,LL &x,LL &y){ 14 if(b==0) { x=1, y=0; return a; } 15 LL ret= Ext_gcd(b,a%b,y,x); 16 y-= a/b*x; 17 return ret; 18 } 19 LL Inv(LL a,int m){ //求逆元 20 LL d,x,y,t= (LL)m; 21 d= Ext_gcd(a,t,x,y); 22 if(d==1) return (x%t+t)%t; 23 return -1; 24 } 25 26 LL Cm(LL n, LL m, LL p) //求组合数 27 { 28 LL a=1, b=1; 29 if(m>n) return 0; 30 while(m) 31 { 32 a=(a*n)%p; 33 b=(b*m)%p; 34 m--; 35 n--; 36 } 37 return (LL)a*Inv(b,p)%p; //(a/b)%p 等价于 a*(b,p)的逆元 38 } 39 40 int Lucas(LL n, LL m, LL p) //把n分段递归求解相乘 41 { 42 if(m==0) return 1; 43 return (LL)Cm(n%p,m%p,p)*(LL)Lucas(n/p,m/p,p)%p; 44 } 45 46 int main() 47 { 48 //freopen("in.txt","r",stdin) ; 49 int T; 50 cin >> T; 51 while(T--) 52 { 53 cin>>n>>m>>p ; 54 cout<<Lucas(n+m,m,p)<<endl ; 55 } 56 return 0; 57 }