题目大意就是求N个数的排列中有多少个前M个数恰有M-K个错牌。这里可以先从前M个数中选K个数不变,即C(M,K)种。然后问题就转化为求n个数前m个数错排的排列数。
递推方程为:f[i][j]=(i-j)*f[i-1][j-1]+(j-1)*f[i-1][j-2];
边界条件为:f[i][0]=i!。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
const LL mod=1000000007;
const int maxn=1010;
LL C[maxn][maxn],dp[maxn][maxn];
void init()
{
memset(C,0,sizeof(C));
memset(dp,0,sizeof(dp));
C[0][0]=1;
for(int i=0;i<=1000;i++)
{
C[i][0]=C[i][i]=1;
for(int j=1;j<i;j++)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
}
dp[0][0]=1;
dp[1][0]=1;dp[1][1]=0;
for(int i=2;i<=1000;i++)
{
dp[i][0]=dp[i-1][0]*i%mod;
for(int j=1;j<=i;j++)
{
dp[i][j]=(dp[i][j]+(i-j)*dp[i-1][j-1])%mod;
if(j>=2) dp[i][j]=(dp[i][j]+(j-1)*dp[i-1][j-2])%mod;
}
}
}
int main()
{
//freopen("in.txt","r",stdin);
init();
int T,kase=1;
int n,m,k;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&m,&k);
printf("Case %d: %lld\n",kase++,C[m][k]*dp[n-k][m-k]%mod);
}
return 0;
}