简要题意:
求有多少种 (1) 到 (n) 的排列 (a),满足序列恰好有 (m) 个位置 (i),使得 (a_i = i).
答案对 (10^9 + 7) 取模。
分析:
首先考虑是哪 (m) 个位置,于是这有 (dbinom{n}{m}) 种情况。
然后就是在 (n-m) 个位置上错位排序的情况。这不就是数列 (D)?
(D(n)) 表示使得 (a_i ot = 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
",C(n,m),D[n-m]);
printf("%lld
",C(n,m)*D[n-m]%MOD);
}
return 0;
}