题目大意 多组数据,每组数据给定三个正整数 (n,m,k),请求出 (1,2,cdots,n) 的所有排列中前 (m) 个数正好有 (k) 个在原位置的个数。
分析 这道题很有意思,需要用到一个奇妙的东西叫做错排。所谓错排,就是一个 (1,2,cdots,n) 的序列的所有数都不在自己位置上的排列数,记做 (D(n))。这个错排和这道题有什么关系呢?假设我们选出了前 (m) 个数中的 (k) 个在原位置上的数,则剩下的 (m-k) 个都不在原位置上。所以我们可以枚举后 (n-m) 个数中有多少个数在原位置上,那么剩下的 (n-m-i) 个就都不在自己原来的位置上。也就是说这 (n-m-i) 个数和前面的 (m-k) 个数构成一个错排,方案数为 (D(n-k-i))。那么最终的答案就是
[ans=C_m^ksum_{i=0}^{n-m}C_{n-m}^i D(n-k-i)
]
至于 (D(n)) 的计算,可以通过如下方式得到:我们考虑把 (1) 放到 (k) 的位置,这一共有 (n-1) 种选法。那么对 (k) 进行讨论。如果将 (k) 放到 (1) 的位置,那么方案数就是剩下 (n-2) 个数的错排,为 (D(n-2)),如果不是,则先将 (k) 放到 (1) 的位置,那么 (k) 和剩下 (n-2) 个数构成一个 (n-1) 的错排,方案数为 (D(n-1))。就是说,有
[D(n)=(n-1)(D(n-1)+D(n-2))
]
初始条件为 (D(0)=1,D(1)=0)。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1E+9 + 7;
int T;
ll n, m, k, ans;
ll D[1005], C[1005][1005];
void Init()
{
C[0][0] = 1;
for(int i = 1; i <= 1000; ++i) {
C[i][0] = 1;
for(int j = 1; j <= 1000; ++j)
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod;
}
D[0] = 1;
for(int i = 2; i <= 1000; ++i)
D[i] = (i - 1) * (D[i - 1] + D[i - 2]) % mod;
}
int main()
{
Init();
int t = 0;
scanf("%d", &T);
while(T--) {
ans = 0;
scanf("%lld%lld%lld", &n, &m, &k);
for(int i = 0; i <= n - m; ++i)
ans = (ans + C[n - m][i] * D[n - k - i]) % mod;
printf("Case %d: %lld
", ++t, ans * C[m][k] % mod);
}
}