P4071 [SDOI2016]排列计数 题解

原题链接

简要题意:

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

答案对 \(10^9 + 7\) 取模。

分析:

首先考虑是哪 \(m\) 个位置,于是这有 \(\dbinom{n}{m}\) 种情况。

然后就是在 \(n-m\) 个位置上错位排序的情况。这不就是数列 \(D\)

\(D(n)\) 表示使得 \(a_i \not = i (\forall i \in [1,n])\) 的长度为 \(n\) 的排列个数。

学过组合数学的大概都知道吧,显然我们有

\[D(n) = n D(n-1) + (-1)^n \]

于是递推。

组合数的话,预处理阶乘和逆元就好了。

时间复杂度:\(\mathcal{O}(T \log n + n)\).

#include <bits/stdc++.h>
using namespace std;

const int N=1e6+1;
typedef long long ll;
const ll MOD=1e9+7;

int T; ll n,m;
ll D[N],inv[N];

inline ll pw(ll x,ll y) {
	ll ans=1; while(y) {
		if(y&1) ans=ans*x%MOD;
		x=x*x%MOD; y>>=1;
	} return ans;
}

inline ll C(ll x,ll y) {
	return inv[x]*pw(inv[y]*inv[x-y]%MOD,MOD-2)%MOD;
}

int main() {
	scanf("%d",&T);
	D[0]=1; inv[0]=1;
	for(int i=1;i<N;i++) D[i]=(i*D[i-1]+pw(-1,i))%MOD;
	for(int i=1;i<N;i++) inv[i]=inv[i-1]*i%MOD;
	while(T--) {
		scanf("%lld %lld",&n,&m);
//		printf("%lld %lld\n",C(n,m),D[n-m]);
		printf("%lld\n",C(n,m)*D[n-m]%MOD);
	}
	return 0;
}
posted @ 2021-11-03 22:28  bifanwen  阅读(31)  评论(0编辑  收藏  举报