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 }