hdu 3037 Saving Beans(组合数取模)

来源:互联网 发布:邢台程序员招聘 编辑:程序博客网 时间:2024/05/05 14:41

Saving Beans

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3697    Accepted Submission(s): 1424


Problem Description
Although winter is far away, squirrels have to work day and night to save beans. They need plenty of food to get through those long cold days. After some time the squirrel family thinks that they have to solve a problem. They suppose that they will save beans in n different trees. However, since the food is not sufficient nowadays, they will get no more than m beans. They want to know that how many ways there are to save no more than m beans (they are the same) in n trees.

Now they turn to you for help, you should give them the answer. The result may be extremely huge; you should output the result modulo p, because squirrels can’t recognize large numbers.
 

Input
The first line contains one integer T, means the number of cases.

Then followed T lines, each line contains three integers n, m, p, means that squirrels will save no more than m same beans in n different trees, 1 <= n, m <= 1000000000, 1 < p < 100000 and p is guaranteed to be a prime.
 

Output
You should output the answer modulo p.
 

Sample Input
21 2 52 1 5
 

Sample Output
33
Hint
HintFor sample 1, squirrels will put no more than 2 beans in one tree. Since trees are different, we can label them as 1, 2 … and so on. The 3 ways are: put no beans, put 1 bean in tree 1 and put 2 beans in tree 1. For sample 2, the 3 ways are: put no beans, put 1 bean in tree 1 and put 1 bean in tree 2.
 

Source
2009 Multi-University Training Contest 13 - Host by HIT
 

Recommend
gaojie   |   We have carefully selected several similar problems for you:  3033 3038 3036 3035 3034 
 

Statistic | Submit | Discuss | Note

题解:组合数取模(lucas定理)

题目大意:从n棵不同的树上取不超过M颗豆子(豆子无差异),有多少种取法。

题目可以转换成  x1+x2+……+xn=m 有多少组解,m在题中可以取0~m。

对于  x1+x2+……+xn=m,需要利用到插板法。

插板法:插板法就是在n个元素间的(n-1)个空中插入 若干个(b)个板,可以把n个元素分成(b+1)组的方法。 
应用插板法必须满足三个条件: 
(1) 这n个元素必须互不相异 
(2) 所分成的每一组至少分得一个元素 
(3) 分成的组别彼此相异

那么这相当于是m 个元素,插入n个板子,但是所分成的每组元素可以剩余,条件(2)不满足,此时如果在3个箱子种各预先插入1个元素,则问题就等价于把n+m个元素插入n个板子,此时的答案就是C(n+m-1,n-1) =C(n+m-1,m) (C(N,M)=C(N,N-M))  。

则题目解的个数可以转换成求   sum=C(n+m-1,0)+C(n+m-1,1)+C(n+m-1,2)……+C(n+m-1,m)利用公式C(n,r)=C(n-1,r)+C(n-1,r-1)  == >  sum=C(n+m,m)。

sum=C(n+m-1,0)(m=0)+C(n+m-1,1)(m=1)+C(n+m-1,2)(m=2)……+C(n+m-1,m)(m=m)

       =C(n-1,0)+C(n,1)+C(n+1,2)....+C(n-m-1,m)

因为C(n-1,0)=C(n,0)=1

所以式子可以利用C(n,r)=C(n-1,r)+C(n-1,r-1) ,进行化简最终得到C(n+m,m)

现在就是要求C(n+m,m)%p。

lucas定理:

A、B是非负整数,p是质数。AB写成p进制:A=a[n]a[n-1]...a[0],B=b[n]b[n-1]...b[0]。
则组合数C(A,B)与C(a[n],b[n])*C(a[n-1],b[n-1])*...*C(a[0],b[0])  modp同余

即:Lucas(n,m,p)=c(n%p,m%p)*Lucas(n/p,m/p,p) 

但是lucas定理只有在A,B<=10^18,P为质数,p<=10^5时才可以使用(如果p<=10^9,但是n,m较小的话,貌似也可以用lucas定理,但是没法预处理阶乘,只能单独计算组合数,总之具体情况具体分析)

这道题的话可以先预处理出阶乘,根据公式c(n,m)=n!/(m!(n-m)!),因为是模意义下所以可以转化为n!*inv(m!(n-m)!)  mod p,因为p是质数,根据费马小定理,可得  inv(m!(n-m)!)=(m!(n-m)!)^p-2,可用快速幂求解。

#include<iostream>#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>#define ll long longusing namespace std;ll k[500003],n,m,p;int t;void calc(ll p){   k[0]=1;   for (ll i=1;i<=p;i++)    k[i]=(ll)k[i-1]*i%p;}ll quick(ll num,ll x){ll  ans=1; ll base=(ll)num%p; while(x){if (x&1) ans=ans*base%p;x>>=1;base=base*base%p;}return ans%p;}ll c(ll x,ll y){if (y>x) return 0;return (ll)k[x]*quick(k[y]*k[x-y],p-2)%p;}ll lucas(ll n,ll m,ll p){if (m==0) return 1;return (ll)c(n%p,m%p)*lucas(n/p,m/p,p)%p;}int main(){freopen("a.in","r",stdin);freopen("my.out","w",stdout);scanf("%d",&t);for (int i=1;i<=t;i++) { scanf("%I64d%I64d%I64d",&n,&m,&p); calc(p); printf("%I64d\n",lucas(n+m,m,p)%p); }}



0 0