http://acm.hdu.edu.cn/showproblem.php?pid=5201
题意:给你n个桃子要你分给m只猴子,猴子可以得0个桃子,问有多少种方法,但是有一个限制条件: 第一只猴子分得的桃子数量一定大于其他猴子的桃子数。
思路:首先部门不考虑限制条件,那么这个问题就非常简单了,n个物品分成m组,允许某些组为空(这不就是隔板法吗!~~~!),简单答案就是组合:C(n+m-1,m-1);
现在我们来考虑限制条件,由于至少k只猴子的桃子数大于第一只猴子的桃子数比较好算,假设第一只猴子得到了x个桃子,那么在剩下的m-1只猴子中选取 k只猴子也得到x个桃子,这个方法数为:C(m-1,k),
还剩下t=n-(x+1)*k个桃子,把它分给除第一只猴子外的m-1猴子,C(t+m-1,m-2);利用容斥定理可以得到最后的答案=总答案-至少一只猴子的桃子数大于第一只猴子+至少两只猴子的桃子数大于第一只猴子-....+....
注意:组合数里面的n,m比较大,求阶乘的时候不好算,可以使用费马小定理先预处理了这些阶乘!a^(p-2)=a^-1(mod p) (p为素数)。
参考:http://blog.csdn.net/tc_to_top/article/details/48579971
1 #include<cstdio> 2 #include<algorithm> 3 #include<queue> 4 using namespace std; 5 typedef long long ll; 6 const int mod=1e9+7; 7 const int maxn=2e5+10; 8 9 int n,m; 10 ll fz[maxn];///分子的阶乘 11 ll fm[maxn];///(分母的阶乘)使用费马小定理求出分母的逆元 12 ///C(x,y)=fz[x]*fm[y]*fm[x-y]; 13 ll Q_pow(ll x,ll y)///x^y 14 { 15 ll cnt=1; 16 while(y) 17 { 18 if(y&1) 19 cnt=cnt*x%mod; 20 x=x*x%mod; 21 y>>=1; 22 } 23 return cnt%mod; 24 } 25 void init() 26 { 27 fz[0]=fz[1]=1; 28 fm[0]=fm[1]=1; 29 for(int i=2;i<maxn-1;i++) 30 { 31 fz[i]=i*fz[i-1]%mod; 32 fm[i]=Q_pow(fz[i],mod-2);///费马小定理(P为素数):a^(p-2)=a^-1(mod p) 33 } 34 } 35 ll C(int x,int y)///组合数C(x,y) 36 { 37 return fz[x]*fm[y]%mod*fm[x-y]%mod; 38 } 39 ll solve(int x)///第一只猴子得到的桃子数为x 40 { 41 if(x==n) 42 return 1; 43 int k=0; 44 int t; 45 ll ans=0; 46 while(1) 47 { 48 if(k>m-1) 49 break; 50 t=n-(k+1)*x;///剩余桃子数 51 if(t<0)///剩余桃子数肯定不能小于0 52 break; 53 ll cnt=C(m-1,k)*C(t+m-2,m-2)%mod; 54 if(k&1)///k%2==1 55 ans=(ans+mod-cnt)%mod; 56 else 57 ans=(ans+cnt)%mod; 58 k++; 59 } 60 return ans; 61 } 62 int main() 63 { 64 int t; 65 scanf("%d",&t); 66 init(); 67 while(t--) 68 { 69 scanf("%d %d",&n,&m); 70 if(m==1) 71 { 72 printf("1\n"); 73 continue ; 74 } 75 ll ans=0; 76 for(int i=1;i<=n;i++) 77 ans=(ans+solve(i))%mod; 78 printf("%lld\n",ans); 79 } 80 return 0; 81 }