有一个长度为n的1~n的全排列,如果一个数i出现在第i个位置上,则称该数是稳定的,询问恰好有m个数是稳定的全排列的方案数,\(n≤1000000,m≤1000000\)。
解
显然为组合计数题,于是我们来相办法划分问题,如果事先选出m个数让其稳定,不难得知剩下的问题就是一个错排问题,而对于错排问题,设\(f_i\)表示长度为i的错排方案数,我们又有递推公式\(f_i=(i-1)\times(f_{i-1}+f_{i-2})\),边界\(f_2=1\),把两者相乘即答案。
参考代码:
#include <iostream>
#include <cstdio>
#define il inline
#define ri register
#define yyb 1000000007
#define ll long long
using namespace std;
ll f[1000001],jc[1000001],jv[1000001];
il ll pow(ll,ll),c(int,int);
il void read(int&),prepare(int);
int main(){
int lsy;read(lsy),prepare(1000000);
while(lsy--){
int n,m;
read(n),read(m);
printf("%lld\n",c(n,m)*f[n-m]%yyb);
}
return 0;
}
il ll c(int n,int r){
if(n<r)return 0;
return jc[n]*jv[r]%yyb*jv[n-r]%yyb;
}
il ll pow(ll x,ll y){
ll ans(1);while(y){
if(y&1)(ans*=x)%=yyb;
(x*=x)%=yyb,y>>=1;
}return ans;
}
il void prepare(int n){
f[0]=jv[0]=f[2]=jc[0]=1;
for(int i(3);i<=n;++i)
f[i]=(i-1)*(f[i-1]+f[i-2])%yyb;
for(int i(1);i<=n;++i)jc[i]=jc[i-1]*i%yyb;
jv[n]=pow(jc[n],yyb-2);
for(int i(n);i>1;--i)jv[i-1]=jv[i]*i%yyb;
}
il void read(int &x){
x&=0;ri char c;while(c=getchar(),c<'0'||c>'9');
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
}