P4071 (递推+计数)

 

题目传送门

题目大意:

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

题目分析:

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

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

  • [1]:数 n 放在了 k 的位置上 那么 n,k 的位置确定了,可知 Fn+=Fn2
  • [2]:数 n 未放在 k 的位置上,那么 n 一定不能放在 k 的位置,可知 Fn+=Fn1

仔细思考一下,对于 Fn1Fn2 是否会出现重复的情况,答案是不会的。因为对于第二种情况种剔除了 n 在次放在 k 位置上的情况。

根据乘法原理可知 F 的递推式 : Fi=(i1)(Fi1+Fi2)

显而易见最后答案为: ans=CnmFnm

代码实现:

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

注意 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 @   L3067545513  阅读(37)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示