HDU 5201 The Monkey King
题意
有\(n\)个桃子,分给\(m\)个猴子,每只猴子分到的桃子数为非负整数。
要求第一只猴子分到的桃子严格多余剩下\(m-1\)只猴子中任意一只。求分桃子的方案数。
分析
首先枚举第一只猴子分到的桃子\(x\)
不考虑限制条件的话,剩余\(m - 1\)只猴子分\(n - x\)个桃子,其方案数\(P(n - x, m - 1) = C_{n - x + m - 2}^{m - 2}\)
我们在这里用到了隔板法。
隔板法
\(P(n,m)\)表示把\(n\)个桃子分给\(m\)个猴子,其中每只猴子都得到非负整数个桃子。
我们新加入\(m\)个桃子,给每个猴子一个,总共有了\(n + m\)个桃子,每只猴子分到的桃子数变成正整数个。
把\(n + m\)个桃子分成\(m\)份,相当于把\(n + m\)个桃子排成一排,然后选择\(m - 1\)个断点,将桃子分成\(m\)段。
任意两个桃子之间有一个可用的断点,总共有\(n + m - 1\)个断点可以选择。
\(P(n,m) = C_{n + m - 1}^{m - 1}\)
隔板法结束
现在考虑限制条件,即\(m - 1\)只猴子分到的桃子\(c_i < x\)
考虑有几只猴子分到的桃子数\(c_i \geq x\)
假设至少有\(s\)只猴子不满足\(c_i < x\)
则\(c_1 \geq x , c_2 \geq x, \dots, c_s \geq x, c_{s+1} \geq 0, \dots, c_{m-1} \geq 0\) (这里强行把\(s\)只猴子排在最前面,实际上是随机的\(s\)只猴子)
\(P(n,m)\)处理的是\(c_1 \geq 0, c_2 \geq 0, \dots, c_{m} \geq 0\)的分法问题,我们需要调整一下上面的式子,让其变为与\(P\)函数相关的式子。
\(g(n - x, m - 1, s)\)表示\(c_1 \geq x , c_2 \geq x, \dots, c_s \geq x, c_{s+1} \geq 0, \dots, c_{m-1} \geq 0\)的分法。
\(g(n - x, m - 1, s) = g(n - x - s * x, m - 1, 0) = P(n - x - s * x , m - 1)\)
即我们对\(s\)只猴子中的每一只猴子都拿走\(x\)个桃子,使其变为\(c_i \geq 0\)的形式。同时总桃子数减少\(s * x\)。
所以当有\(s\)只猴子不满足条件时,方案数为\(C_{m-1}^{s} * P(n - x - s * x , m - 1)\)
容斥
答案\(Ans = \sum_{s = 0}^{m - 1} (-1)^{s} C_{m-1}^{s} * P(n - x - s * x , m - 1)\)
答案等于至少\(0\)猴子-至少\(1\)猴子+至少\(2\)猴子......
注意各种不合实际的情况要break
容斥结束
特判\(n=1\)和\(m=1\)!
#include<bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
const int N = 1e6 + 50;
int n,m;
int fac[N + 10], inv[N + 10];
int ksm(int x,int y){
int z = 1;
while(y){
if(y & 1) z = 1ll * z * x % mod;
y >>= 1;
x = 1ll * x * x % mod;
}
return z;
}
int C(int n,int m){
if(n < m || m < 0 || n < 0) return 0;
return 1ll * fac[n] * inv[m] % mod * inv[n - m] % mod;
}
int P(int n,int m){
return C(n + m - 1, m - 1);
}
signed main(){
fac[0] = 1; for(int i = 1; i <= N; ++ i) fac[i] = 1ll * fac[i - 1] * i % mod;
inv[N] = ksm(fac[N], mod - 2); for(int i = N - 1; i >= 0; -- i) inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
int T; scanf("%d",&T);
while(T --){
scanf("%d%d",&n,&m);
if(n == 1 || m == 1) { puts("1"); continue; }
int ans = 0;
for(int i = 1; i <= n; ++ i){
if(1ll * m * i - m + 1 < n) continue;
int ret = P(n - i, m - 1); int flag = -1;
for(int j = 1; j < m && 1ll * (j + 1) * i <= n; ++ j){
if(n - i - 1ll * j * i < 0) break;
int x = 1ll * C(m - 1, j) * P(n - i - j * i, m - 1) % mod;
x = x * flag;
ret = ((ret + x) % mod + mod) % mod;
flag *= -1;
}
ans = (ans + ret) % mod;
}
printf("%d\n",ans);
}
return 0;
}