hdu5201 n个桃子分给m个猴子使第一个猴子的桃子严格最大的方案:容斥/组合数/费马小定理求阶层逆元

先预处理阶层逆元算组合数=

首先来枚举第一个猴子得到的桃子x,这样就变成了剩下的n-x个桃子分给m-1个猴子且不能有猴子得到桃子大于x的方案,最后求和

>>先来看这样一个简单问题:n个桃子分给m个猴子有多少种方案?很简单隔板法因为允许为0所以是C(x-1+y,y)

回到这个问题,再来枚举剩下的m-1个猴子有i个得到的桃子数量大于等于第一个,有重复?容斥计算啊!

++费马小定理的逆元,发现素数一直没用过挺方便的==

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<algorithm>
 4 using namespace std;
 5 #define MOD 1000000007
 6 #define LL long long
 7 LL f[200005],invf[200005];
 8 LL quick(LL a,LL x)
 9 {
10   LL ans=1;
11   while (x){
12     if (x%2) ans=ans*a%MOD;
13     a=a*a%MOD;
14     x/=2;
15   }
16   return ans;
17 }
18 LL C(LL x,LL y)
19 {
20   return f[x]*invf[y]%MOD*invf[x-y]%MOD;
21 }
22 LL cal(LL x,LL n,LL m)
23 {
24   LL i,tmp;
25   if (n==0) return 1;
26   LL ans=0;
27   for (i=0;i<=m&&i*x<=n;i++){
28     LL tmp=C(m,i)*C(n-i*x+m-1,m-1)%MOD;
29     if (i%2==0) ans=(ans+tmp)%MOD;
30     else ans=(ans-tmp+MOD)%MOD;
31   }
32   return ans;
33 }
34 int main()
35 {
36   LL i,n,m,ans;
37   int T;
38   f[0]=f[1]=invf[0]=invf[1]=1;
39   for (i=2;i<=200002;i++){
40     f[i]=f[i-1]*i%MOD;
41     invf[i]=quick(f[i],MOD-2);
42   }
43   scanf("%d",&T);
44   while (T--){
45     scanf("%I64d%I64d",&n,&m);
46     if (m==1) { printf("1\n"); continue; }
47     ans=0;
48     for (i=1;i<=n;i++)
49       ans=(ans+cal(i,n-i,m-1))%MOD;
50     printf("%I64d\n",ans);
51   }
52   return 0;
53 }
View Code

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5201

posted on 2015-04-07 23:40  xiao_xin  阅读(500)  评论(0编辑  收藏  举报

导航