题目链接

题目大意:问有多少个\(1-n\)的排列\(A\)恰好有\(m\)个数满足\(A_i=i\)

错排,计数


分析:

首先我们选\(m\)个数有\(C_n^m\)种,那么我们要求答案合法就必须使得剩下的数都不满足\(A_i=i\),也就是我们要求\(n-m\)个数错排的方案数,假设\(D_n\)表示\(n\)个数错排的方案数,我们的答案就是\(C_n^m\times D_{n-m}\)

关键是\(D\)的求法,对于一个位置\(n\)有两种可能,前\(n-1\)个位置都错了,前\(n-1\)个位置有一个是对的,这两种情况都可以通过一次交换得到全错排情况,其它情况不行

因此\(D_n=(n-1)\times(D_{n-1}+D_{n-2})\)

预处理一下阶乘及其逆元,和错排数\(D\)即可

注意当\(n=m\)时答案为\(1\)

#include <cstdio>
using namespace std;
const int mod = 1e9 + 7,maxn = 1e6;
typedef long long ll;
ll D[maxn + 100],fac[maxn + 100],facinv[maxn + 100];
inline ll mul(ll a,ll b){return a * b % mod;}
inline ll qpow(ll a,ll b){
	ll res = 1,base = a % mod;
	while(b){
		if(b & 1)res = mul(res,base);
		base = mul(base,base);
		b >>= 1;
	}
	return res;
}
inline ll inv(ll x){return qpow(x,mod - 2);}
inline ll C(ll n,ll m){return mul(fac[n],mul(facinv[m],facinv[n - m]));}
inline void init(){
	fac[0] = 1,facinv[0] = 1;
	for(int i = 1;i <= maxn;i++){
		fac[i] = mul(fac[i - 1],i);
		facinv[i] = inv(fac[i]);
	}
	D[0] = D[2] = 1;
	for(int i = 3;i <= maxn;i++)
		D[i] = mul(i - 1,D[i - 1] + D[i - 2]);
}
int t,n,m;
int main(){
	init();
	scanf("%d",&t);
	for(int i = 1;i <= t;i++)
		scanf("%d %d",&n,&m),printf("%lld\n",mul(C(n,m),D[n - m]));
	return 0;
}	

相关文章:

  • 2021-12-30
  • 2021-12-01
  • 2021-07-13
  • 2021-06-12
  • 2021-08-21
  • 2022-02-11
猜你喜欢
  • 2021-09-25
  • 2022-12-23
  • 2021-09-01
  • 2021-12-11
  • 2021-12-24
  • 2021-09-30
  • 2021-09-11
相关资源
相似解决方案