题解 UVa11481

题目大意 多组数据,每组数据给定三个正整数 \(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^k\sum_{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\n", ++t, ans * C[m][k] % mod);
    }
}

猜你喜欢

转载自www.cnblogs.com/whx1003/p/12333143.html
今日推荐