题目传送门
很明显,这是一个需要用到组合数来求解的题,并且还要知道 错位排列是什么
首先很容易得出一个式子C(n,m)*(....),括号内暂时不管,就是先假设符合条件的m个数字的摆放的位置,接下来处理剩余的n-m个数字,简记为K。
对于剩下的K个数字,因为它们的排列一定不满足A[i]=i,所以就可以用到错位排列的知识。
记D[i]为有i个数的错位排列的个数,那么由乘法原理可得答案为C(n,m)*D(K)。
接下来
1.组合数的求法,用阶乘+逆元预处理求组合数,先预处理出数据范围内的阶乘,根据费马小定理
(a MOD p意义下的逆元等于a^(p-2)%p),求出fact(i)的逆元inv[i]。最后O(1)回答。
2.对于错排列的求法,有这么一个递推公式D[i]=(i-1)*(D[i-1]+D[i-2])
至此问题基本得到解决。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#define MOD 1000000007
#define MAXN 1000007
#define N 1000000
#define LL long long
using namespace std;
LL n,m;
LL fact[MAXN],inv[MAXN];
LL f[MAXN],tmp;
//快速幂,求逆元
inline LL Q_pow(LL x,LL y){
LL res=1;
while(y){
if(y&1) res=(res*x)%MOD;
x=(x*x)%MOD;
y>>=1;
}
return res;
}
//预处理,求阶乘及其逆元
inline void init(){
f[0]=1;f[1]=0;f[2]=1; //初始化,为了处理掉一些特殊情况
fact[0]=1;inv[0]=1; //
for(LL i=1;i<=N;++i){
fact[i]=fact[i-1]*i%MOD;
inv[i]=Q_pow(fact[i],MOD-2);
}
for(LL i=3;i<=N;++i){
f[i]=((i-1)*(f[i-1]+f[i-2]))%MOD;
}
}
inline void solve(){
LL T;
scanf("%lld",&T);
while(T--){
scanf("%lld %lld",&n,&m);
tmp=((fact[n]*inv[m]%MOD)*inv[n-m])%MOD;
tmp=(tmp*f[n-m])%MOD;
printf("%lld\n",tmp);
}
}
int main(){
init();
solve();
return 0;
}