错排
错排问题:
定义:
给定 \(n\) 元素集合 \(X\) ,它的每一个元素都有一个特定的位置.
而现在要求求出集合 \(X\) 的排列中没有一个元素在它指定位置上的排列的数目.
这样的问题叫做错排问题
解决:
我们设 \(D[n]\) 表示 \(n\) 个元素的错排数
根据容斥原理,我们能得到以下推导:
\[D_n=n!(1-\frac{1}{1!}+\frac{1}{2!}+...+(-1)^n \frac{1}{n!}
\]
还可以通过递推式得到:
我们记 \(n\) 封信错排数为 \(D_n\), 利用递推得到:
由于需要错排,第一封信可以装到除了第一个信封的任意位置,我们设装到第二个信封。那么现在有两种情况:
-
第二封信装到第一个信封,问题变为 \((n-2)\) 封信的错排问题,即 \(D_{n-2}\)
-
第二封信不装到第一封信,那么现在就是第 \(i\) 封不能装到第 \(i(i>2)\) 个,而第二封不能装到第 \(1\) 个信封,所以就是 \((n-1)\) 封信的错排问题,即 \(D_{n-1}\).
由于第三,四...封信都可以像第二封信一样,形成上面两种情况,因此则能得出递推式:
\[D_n=(n-1)(D_{n-1}+D_{n-2})
\]
两边同时除以 \(n!\) ,转化以下就可以得到上面式子。
例题:
题意:
求有多少种 \(1,n\) 的排列 \(a\),满足序列恰好有 \(m\) 个位置 \(i\),使得 \(a_i = i\)
分析:
\(m\) 个位置适合,有 \(n\) 个数,可以看成 \(C_n^m\) 的组合。
剩下 \(n-m\) 个数,要求是错排,那么就是 \(D[n-m]\) 。
相乘即可。
注意判断一下特殊情况。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e9+7,N=1e6+5;
int T;
int n,m;
int inv[N],mul[N],D[N];
int qmi(int a,int b){
int res=1;
while(b){
if(b&1) res=res*a%mod;
b>>=1; a=a*a%mod;
}
return res;
}
void init(int n){
mul[1]=1;
for(int i=2;i<=n;i++) mul[i]=(mul[i-1]*i)%mod;
inv[n]=qmi(mul[n],mod-2);
for(int i=n-1;~i;i--) inv[i]=(inv[i+1]*(i+1))%mod;
D[1]=0;D[2]=1;
for(int i=3;i<=n;i++) D[i]=(i-1)*(D[i-1]+D[i-2])%mod;
}
int C(int n,int m){
if(m>n) return -1;
if(m==n) return 1;
return mul[n]*inv[n-m]%mod*inv[m]%mod;
}
signed main(){
cin>>T;
init(1000000);
while(T--){
scanf("%lld%lld",&n,&m);
if(n==m+1) puts("0");
else if(m==n) puts("1");
else if(m==0) printf("%lld\n",D[n]);
else printf("%lld\n",C(n,m)*D[n-m]%mod);
}
system("pause");
return 0;
}
不关注的有难了😠😠😠https://b23.tv/hoXKV9