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 }
View Code

 

posted @ 2015-09-20 20:53  __Meng  阅读(173)  评论(0编辑  收藏  举报