排列计数(组合数学,错位排列)

题目:题目链接

求有多少种长度为 n 的序列 A,满足以下条件:

1∼n 这 n 个数在序列中各出现了一次; 若第 i 个数 Ai 的值为 i,则称 i 是稳定的。序列恰好有 m 个数是稳定的。
满足条件的序列可能很多,序列数对 109+7 取模。

题目大意:
求长度为n的序列中有m个数是满足 a [ i ] = i a[i]=i a[i]=i 的序列方案数。

解题思路:
题意转换一下就是我们从n个位置中找m个位置让他们满足 a [ i ] = i a[i]=i a[i]=i 然后剩下的 n − m n-m nm 个数全排列的方案数。

现在就是求出m个位置满足 a [ i ] = i a[i]=i a[i]=i 的方案数 再乘以 n − m n-m nm 个数全排列的方案数。

①.求出m个位置满足 a [ i ] = i a[i]=i a[i]=i 的方案数

错位排列: 状态转移错位排序超详解

用 d p [ i ] 来 表 示 前 i 个 数 他 们 的 错 排 方 案 数 用dp[i]来表示前i个数他们的错排方案数 dp[i]i
d p [ i ] = ( i − 1 ) ∗ ( d p [ i − 1 ] + d p [ i − 2 ] ) dp[i]=(i-1)*(dp[i-1]+dp[i-2]) dp[i]=(i1)(dp[i1]+dp[i2])(上面链接超详解)

②.求 n − m n-m nm个数的全排列,这个用组合数预处理一下就好了。
放个模板

typedef long long ll;
const int maxn = 1e6 + 5;
int mod = 1e9 + 7;
ll f[maxn+5];
ll qpow(ll a, ll b) {
	ll ans = 1, base = a;
	while(b) {
		if(b&1) ans = ans * base % mod;
		base = base * base % mod;
		b >>= 1;
	}
	return ans;
}
void init() {
	f[0] = 1;
	for(int i=1; i<=maxn; i++) {
		f[i] = f[i-1] * i % mod;
	}
}
ll cal(ll n, ll m) {
	if(n<m) return 0;
	return 1ll*f[n]*qpow(f[m], mod-2)%mod*qpow(f[n-m], mod-2)%mod;
}

AC代码:

#include <algorithm>
#include <cmath>
#include <iostream> 
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 5;
int mod = 1e9 + 7;
ll f[maxn+5];
ll qpow(ll a, ll b){
	ll ans = 1, base = a;
	while(b){
		if(b&1) ans = ans * base % mod;
		base = base * base % mod;
		b >>= 1;
	}
	return ans;
}
void init(){
	f[0] = 1;
	for(int i=1; i<=maxn; i++){
		f[i] = f[i-1] * i % mod;
	}
}
ll cal(ll n, ll m){ //逆元法处理组合数
	if(n<m) return 0;
	return 1ll*f[n]*qpow(f[m], mod-2)%mod*qpow(f[n-m], mod-2)%mod;
}
ll dp[maxn];

int main(){
	ll n, t, m;
	init();
	cin >> t;
	dp[1] = 0; dp[2] = 1;
	for(int i=3; i<=maxn; i++){ // 计算错排
		dp[i] = (i-1) * (dp[i-1] + dp[i-2]) % mod;
	}
	while(t--){
		cin >> n >> m;
		
		if(n == m) cout << 1 << endl;
		else cout << cal(n, m) % mod * dp[n-m] % mod << endl;
	}
	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值