P4071 [SDOI2016]排列计数
题目大意:求\(n\)的错位排序数
有全排列\(1\sim n,\left \{ 1,2,3,\dots ,n\right \}\)对于\(a[i]\ne i\)
我们称为错位排列
有递推式
\[D_n = (n-1)(D_{n-1} + D_{n-2})
\]
证明:
设有\(n\)的全排列\(\left\{1,2,3,4, \dots n \right \}\)
当\(n = 1\)时\(D_n\)为0
当\(n = 2\)时\(D_n\)为1
设最后1位为\(x_n\)
有\(n(n>2),k(k \ne n)\)n在第\(k\)位
- 若\(k\)在\(n\)上有 \(D_{(n-2)}\)种错排
- 当\(k\)不排在第\(n\)位时,那么将第\(n\)位重新考虑成一个新的“第\(k\)位”,这时的包括\(k\)在内的剩下\(n-1\)个数的每一种错排,都等价于只有\(n-1\)个数时的错排(只是其中的第\(k\)位会换成第\(n\)位)。其错排数为\(D_{n-1}\)。
因为\(k\)有\(n-1\)种取法所以\(D_n = (n-1)(D_{n-1} + D_{n-2})\)
#include <iostream>
using namespace std;
typedef long long ll;
ll f[25];
int main()
{
int n;
scanf("%d",&n);
f[1] = 0,f[2] = 1;
for(int i = 3 ; i <= n ; i ++ )
{
f[i] = (i - 1)*(f[i - 1] + f[i - 2]);
}
printf("%lld",f[n]);
}
然后我们就可写下面一道题了 [SDOI2016]排列计数
求有多少种 \(1\) 到 \(n\) 的排列 \(a\),满足序列恰好有 \(m\) 个位置 \(i\),使得 \(a_i = i\)。
答案对 \(10^9 + 7\) 取模。
题目大意:
题意是让长度为n的序列有且仅有有m个数在原本的位置上.
也就是求除\(n-m\)的错位排序数乘上\(m\)个数的位置\(C^n_m\)
\(C^n_m \times D_{n-m}\)
通过预处理阶乘逆元和错位排序数,在进行查表操作即可
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int N = 1000010;
const int mod = 1e9 + 7;
ll db[N];
ll invfact[N],fact[N];
int power(int a,int b,int p)
{
int res = 1;
while(b)
{
if(b&1)res = (ll)res * a % p;
a = (ll) a * a % p;
b >>= 1;
}
return res;
}
void facts()
{
fact[0] = invfact[0] = 1;
for(int i = 1 ; i <= N ; i ++ )
{
fact[i] = (ll)fact[i - 1] * i % mod;
invfact[i] = (ll)invfact[i - 1] * power(i,mod - 2,mod) % mod;
}
}
void dbs()
{
db[1] = 0,db[2] = 1;
for(int i = 3 ; i <= N ; i ++ )
{
db[i] =(i - 1)*(db[i - 1] + db[ i - 2 ]) % mod;
}
}
int main()
{
int T,n,m;
scanf("%d",&T);
facts();
dbs();
while(T--)
{
scanf("%d%d",&n,&m);
if(n == m){printf("1\n");continue;}
ll cnm = (ll)fact[n] * invfact[m] % mod * invfact[n - m] % mod;
printf("%lld\n",(ll)cnm * db[n-m]%mod);
}
}
“风雪越是呼啸,雪莲越是绽放”

浙公网安备 33010602011771号