http://acm.hdu.edu.cn/showproblem.php?pid=4045
题意 :
有N台机器,要从中选出r台来,这r台机器之间必须要满足两两之间的编号不小于k,选出r台机器之后,要把这r台机器分成不超过m组,问共有多少中满足条件的种数。
思路:
这是一个组合数学计数的问题,我们可以将原问题分成两个子问题来解决: Q1:从n个数中选出r个数,要求两两之间的编号差不小于k;Q2:将r个数分成不超过m组,共有多少种方法。Q2可以用第二类Strling数来求解,我们只需要先打出Strling数的表,那么ans2 = S[r][1] + S[r][2] + S[r][3] + ... + S[r][ m ] ,这里需要注意的是第二类Strling数的分组是不能有空盒的分组,并且球之间是有区别的。 接着我们来解决Q1,我们可以这样来想,我们把n台机器看作完全相同的n台,那么我们可以先选出r台机器来,将他们排成一排,为了确保两两之间的编号不小于k,那么我们在任意两台机器之间插入k-1台机器,这样我们就剩下a ( n - r - (r-1) * (k-1) )台机器了,这a台机器可以插入到上面的 r 台机器之间的任意位置,并且前后也可以插入, 这样我们就相当于是要求下面方程的解的组数:x1 + x2 + ... xr+1 = a
每个xi都可以为0 ,因为可以为0 , 我们就不能用简单的隔板法来求了,因为隔板法是要求每个xi都>=1的,我们可以先将每个xi加1,然后再用隔板法就可以了。最后ans1 = C[a + r][r] , 那么最后的答案就是ans1 * ans2 了。
代码:
#include <stdio.h>
#include <string.h>
typedef __int64 LL ;
const LL Mod = 1000000007 ;
const int MAXN = 1010 ;
LL n ,r , k , m ;
LL S[MAXN][MAXN] ;
LL C[MAXN][MAXN] ;
void init(){
S[1][1] = 1 ;
for(int i=2;i<MAXN ;i++){
S[i][1] = S[i][i] = 1 ;
for(int j=2;j<i;j++){
S[i][j] = ( S[i-1][j-1] + ( j*S[i-1][j] % Mod ) ) % Mod;
}
}
C[0][0] = C[1][0] = C[1][1] = 1 ;
for(int i=2;i<MAXN;i++){
C[i][0] = 1 ;
for(int j=1;j<=i;j++){
C[i][j] = C[i-1][j-1] ;
if( j<=i-1 )
C[i][j] = ( C[i][j] + C[i-1][j] ) % Mod ;
}
}
}
LL solve(){
if( n<r+(r-1)*(k-1) ) return 0 ;
LL ans1 = C[ n-(r-1)*(k-1) ][r] ;
LL ans2 = 0 ;
for(int j=1;j<=m;j++){
ans2 = ( ans2 + S[r][j] ) % Mod ;
}
return ( ans1 * ans2 ) % Mod ;
}
int main(){
init() ;
while( scanf("%I64d%I64d%I64d%I64d",&n,&r,&k,&m) == 4){
printf("%I64d\n",solve() );
}
return 0 ;
}