• 题解 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^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);
    	}
    }
    
  • 相关阅读:
    利用scanf函数修改内存中任意位置内容
    TSql
    完整性约束及其违约处理
    实现关机、重启、注销
    建立索引的原则
    我只想安静的大便
    格式化GridView特定的值
    SELECT语句执行顺序解析
    CPU对存储器的读写
    Linux常用97条命令
  • 原文地址:https://www.cnblogs.com/whx1003/p/12333143.html
Copyright © 2020-2023  润新知