题目:题目链接
求有多少种长度为 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
n−m 个数全排列的方案数。
现在就是求出m个位置满足 a [ i ] = i a[i]=i a[i]=i 的方案数 再乘以 n − m n-m n−m 个数全排列的方案数。
①.求出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]=(i−1)∗(dp[i−1]+dp[i−2])(上面链接超详解)
②.求
n
−
m
n-m
n−m个数的全排列,这个用组合数预处理一下就好了。
放个模板
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;
}