错排数

定义

考虑一个有 n 个元素的排列,若一个排列中所有的元素都不在自己原来的位置上,那么这样的排列就称为原排列的一个错排。 n 个元素的错排数记为 Dn

对于情况较少的排列,可以使用枚举法。

n=1 时,全排列只有一种,不是错排,D1=0

n=2 时,全排列有两种,即 1221,后者是错排,D2=1

当n=3时,全排列有六种,其中只有 312231 是错排,D3=2。用同样的方法可以知道 D4=9

最小的几个错排数是:D1=0D2=1D3=2D4=9D5=44D6=265D7=1854

递推式:

考虑对于一个数 n,放在了第 i 个位置,那么 i 这个数就可以放在 n 位置,或者除了 i,n 以为的任意一个位置。

(1)i 放在了 n 位置,那么剩下的 n2 个数的排列方法就是 Dn2

(2)i 不放在 n 位置,那么 i 就有 n2 个位置可以去,其余的所有元素也都有 n2 个位置可以去,那么就成为了 n1 个元素的错排问题,方案数为 Dn1

而这样的 i 一共有 n1 个,所以最终的递推式就是:

Dn=(n1)×(Dn1+Dn2)

排列计数

求有多少种 1n 的排列 a,满足序列恰好有 m 个位置 i,使得 ai=i

答案对 109+7 取模。

1n1060m106

思路:

排列中有 m 个位置在原位置上,那么也就是有 nm 个数不在原位置上,这部分的方案数就是 Dnm,而选取这 m 个位置的方案数为 (nm),所以最终的答案为:

Ans=(nm)Dnm

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;
}
posted @   曙诚  阅读(428)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示