组合数学:P4071 排列计数

P4071 排列计数

求有多少种 11nn 的排列 aa,满足序列恰好有 mm 个位置 ii,使得 ai=ia_i = i

答案对 109+710^9 + 7 取模。


考虑转化问题。

恰好有 mm 个位置有 ai=ia_i=i,就是在 nn 个数中选出 mm 个数的无序组合,即 Cnm\large{C_n^m}

除去这 mm 个数,还有 nmn-m 个位置是满足任意 aiia_i\ne i 的,即错排的。

对于错排问题。设 fnf_n 为长度为 nn 的错排序列个数。 假设考虑到第 nn 个信封,初始时暂时把第 nn 封信放在第 nn 个信封中,然后考虑两种情况的递推:

  • 前面 n1n-1 个信封全部装错;
  • 前面 n1n-1 个信封有一个没有装错其余全部装错。 对于第一种情况,前面 n1n-1 个信封全部装错:因为前面 n1n-1 个已经全部装错了,所以第 nn 封只需要与前面任一一个位置交换即可,总共有 fn1×(n1)f_{n-1}\times (n-1) 种情况。

对于第二种情况,前面 n1n-1 个信封有一个没有装错其余全部装错:考虑这种情况的目的在于,若 n-1 个信封中如果有一个没装错,那么把那个没装错的与 nn 交换,即可得到一个全错位排列情况。就可以得到:

fn=(n1)(fn1+fn2)f_n=(n-1)(f_{n-1}+f_{n-2})

选自 oi-wiki

两种情况是满足乘法原理的。同时,可以利用乘法逆元求组合数。

关于乘法逆元:

因为同余不满足同除性,根据费马小定理,若 pp 为素数,gcd(a,p)=1\gcd(a,p)=1,则 ap11(modp)a^{p - 1} \equiv 1 \pmod{p}

再用快速幂就好啦。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6+10,Mod = 1e9+7;
int f[N],fac[N];
int T;
int mi(int x,int k) {
	if(!k) return 1;
	int p=mi(x,k>>1);
	return (p*p%Mod)*(k&1?x:1)%Mod;
}
int ny(int x) {return mi(x,Mod-2);}
int C(int n,int m) {
	return (fac[n]*ny(fac[n-m])%Mod)*ny(fac[m])%Mod;
}
signed main() {
	f[0]=1;
	//这里是因为当 n=m 时,不用考虑错排情况 
	f[2]=1;fac[0]=1,fac[1]=1,fac[2]=2;
	for(int i=3;i<N;i++) {
		f[i]=(i-1LL)*(f[i-1]+f[i-2])%Mod;
		fac[i]=fac[i-1]*i%Mod;
	}
	ios::sync_with_stdio(0); cin.tie(0), cout.tie(0);
	cin>>T;
	while(T--) {
		int n,m;
		cin>>n>>m;
		cout<<(C(n,m)%Mod)*f[n-m]%Mod<<"\n";
	}
	return 0;
}
posted @ 2023-10-27 16:19  cjrqwq  阅读(16)  评论(0编辑  收藏  举报  来源