定义
考虑一个有 \(n\) 个元素的排列,若一个排列中所有的元素都不在自己原来的位置上,那么这样的排列就称为原排列的一个错排。 \(n\) 个元素的错排数记为 \(D_n\)。
对于情况较少的排列,可以使用枚举法。
当 \(n=1\) 时,全排列只有一种,不是错排,\(D_1 = 0\)。
当 \(n=2\) 时,全排列有两种,即 \(1、2\) 和 \(2、1\),后者是错排,\(D_2=1\)。
当n=3时,全排列有六种,其中只有 \(3、1、2\) 和 \(2、3、1\) 是错排,\(D_3=2\)。用同样的方法可以知道 \(D_4=9\)。
最小的几个错排数是:\(D_1 = 0,D_2 = 1,D_3=2,D_4 = 9,D_5 = 44,D_6 = 265,D_7 = 1854\)。
递推式:
考虑对于一个数 \(n\),放在了第 \(i\) 个位置,那么 \(i\) 这个数就可以放在 \(n\) 位置,或者除了 \(i,n\) 以为的任意一个位置。
(1)\(i\) 放在了 \(n\) 位置,那么剩下的 \(n-2\) 个数的排列方法就是 \(D_{n-2}\)。
(2)\(i\) 不放在 \(n\) 位置,那么 \(i\) 就有 \(n-2\) 个位置可以去,其余的所有元素也都有 \(n-2\) 个位置可以去,那么就成为了 \(n-1\) 个元素的错排问题,方案数为 \(D_{n-1}\)。
而这样的 \(i\) 一共有 \(n-1\) 个,所以最终的递推式就是:
\(D_{n}=(n-1)\times (D_{n-1}+D_{n-2})\)。
排列计数
求有多少种 \(1\) 到 \(n\) 的排列 \(a\),满足序列恰好有 \(m\) 个位置 \(i\),使得 \(a_i = i\)。
答案对 \(10^9 + 7\) 取模。
\(1 \leq n \leq 10^6\),\(0 \leq m \leq 10^6\)。
思路:
排列中有 \(m\) 个位置在原位置上,那么也就是有 \(n-m\) 个数不在原位置上,这部分的方案数就是 \(D_{n-m}\),而选取这 \(m\) 个位置的方案数为 \(\binom{n}{m}\),所以最终的答案为:
\(Ans=\binom{n}{m} D_{n-m}\)。
code:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int mod=1e9+7;
const int N=1e6+10;
int fac[N],infac[N],inv[N],n,m,T,d[N];
void init()
{
fac[0]=infac[0]=inv[0]=fac[1]=infac[1]=inv[1]=1;d[1]=0;d[2]=1;
for(int i=2;i<N-1;i++)
{
d[i+1]=1ll*i*(d[i]+d[i-1])%mod;
fac[i]=1ll*i*fac[i-1]%mod;
inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
infac[i]=1ll*infac[i-1]*inv[i]%mod;
}
}
int C(int n,int m){return 1ll*fac[n]*infac[m]%mod*infac[n-m]%mod;}
int main()
{
init();
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
if(n==m) puts("1");
else printf("%d\n",(1ll*C(n,m)*d[n-m]%mod+mod)%mod);
}
return 0;
}
标签:10,排列,排数,int,位置,leq,错排
From: https://www.cnblogs.com/ListenSnow/p/17198838.html