洛谷题单指南-组合数学与计数-P4071 [SDOI2016] 排列计数
原题链接:https://www.luogu.com.cn/problem/P4071
题意解读:1~n的数字排列,有m个不错位,求有多少种排列。
解题思路:
错排问题:1~n的排列中,数字与位置编号不相同称之为错位。
错排的方案数可以用递推计算,设f(n)表示1~n的错排数,
当考虑到数字n,先将n放在第n号位,前面的n-1个数已形成的排列有两种可能:
1、前n-1个数已经是错排,则将n与前n-1任意一个数交换即可得到n个数的错排,方案一共是(n-1) * f(n-1)
2、前n-1数中有一个数没有错排,则将n与这个数交换即可得到n个数的错排,由于前n-1个数有一个没有错排,方案数一共是(n-1)*f(n-2),
根据加法原理,f(n) = (n-1)(f(n-1) + f(n-2))
回到此题,n个数要有m个没有错排,先选出m个数c(n,m),再将n-m个数错排f(n-m),所以一共是c(n,m) * f(n-m)
100分代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6 + 5, MOD = 1e9 + 7;
LL fac[N], inv[N], f[N];
LL T, n, m;
LL ksm(LL a, LL b, LL mod)
{
LL res = 1;
while(b)
{
if(b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
fac[0] = inv[0] = 1;
for(LL i = 1; i <= N; i++)
{
fac[i] = fac[i - 1] * i % MOD; //阶乘的模
inv[i] = inv[i - 1] * ksm(i, MOD - 2, MOD) % MOD; //阶乘的逆元的模
}
f[0] = 1, f[1] = 0;
for(LL i = 2; i <= N; i++) f[i] = (i - 1) * (f[i - 1] + f[i - 2]) % MOD; //错排公式
cin >> T;
while(T--)
{
cin >> n >> m;
LL ans = fac[n] * inv[n - m] % MOD * inv[m] % MOD * f[n - m] % MOD; //C(n,m)*f(n-m)
cout << ans << endl;
}
return 0;
}
浙公网安备 33010602011771号