P4071 (递推+计数)

 

题目传送门

题目大意:

给定两个数 \(n , m\) ,求 \(n\) 的排列中恰好有 \(m\) 个数在对应位置的方案数

题目分析:

对于一个长度为 \(n\) 的排列,我们可以钦定哪几位是在对应位置上,那么问题就变成了在 \(n\) 个数里面选出 \(m\) 个数的方案数 \(C^m_n\),显而易见对于这 \(C^m_n\) 种方案数都是不同的。

那么我们考虑对于其他 \(n - m\) 为错位的情况,我们设 \(F_n\) 为长度为 \(n\) 的全错排序列的方案数 ,那么一定有一个数 \(k\) 放在了 \(n\) 的位置上 , 所以我们考虑对于数 \(n\) 是否放在了某个位置 \(k\) , 我们分为两种情况 :

  • [\(1\)]:数 \(n\) 放在了 \(k\) 的位置上 那么 \(n , k\) 的位置确定了,可知 \(F_n += F_{n - 2}\)
  • [\(2\)]:数 \(n\) 未放在 \(k\) 的位置上,那么 \(n\) 一定不能放在 \(k\) 的位置,可知 \(F_n += F_{n - 1}\)

仔细思考一下,对于 \(F_{n - 1}\)\(F_{n - 2}\) 是否会出现重复的情况,答案是不会的。因为对于第二种情况种剔除了 \(n\) 在次放在 \(k\) 位置上的情况。

根据乘法原理可知 \(F\) 的递推式 : \(F_i = (i - 1) * (F_{i - 1} + F_{i - 2})\)

显而易见最后答案为: \(ans = C^m_n * F_{n - m}\)

代码实现:

我们可以先预处理出 \(1 - N\) 的所有阶乘与它对应的逆元。

注意 \(0! = 1\)

对于 \(F\) 数组递推求解即可

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 1e9 + 7;
const int M = 1e6 + 7;
int jc[M] , inv[M] , f[M];
int Pow(int a , int b) {
	int ans = 1;
	while(b) {
		if(b & 1) ans = ans * a % mod;
		a =a * a % mod;
		b >>= 1;
	}
	return ans;
}
signed main () {
	ios::sync_with_stdio(0),cin.tie(0);
	int t; cin >> t;
	jc[0] = 1 , jc[1] = 1;
	inv[0] = 1;
	for(int i = 1; i <= 1e6; ++ i) {
		jc[i] = jc[i - 1] * i % mod;
		inv[i] = Pow(jc[i] , mod - 2);	
	}
	f[0] = 1 ,f[1] = 0 , f[2] = 1;
	for(int i = 3; i <= 1e6; ++ i) f[i] = (i - 1) * (f[i - 1] + f[i - 2]) % mod;
	while(t --) {
		int n , m; cin >> n >> m;
		cout << (((jc[n] * inv[m] % mod)* inv[n - m] % mod) * f[n - m] % mod) << '\n';
	}
	return 0;	
}
posted @ 2022-09-05 09:40  L3067545513  阅读(35)  评论(0编辑  收藏  举报