1. 程式人生 > >排列計數[luogu4071][逆元][錯排列]

排列計數[luogu4071][逆元][錯排列]

傳送門

ans=C_{_{}^{N}}^{M}*D[N-M]

D[i]表示錯排列個數 , 可以證明

D[i]=(i-1)*(D[i-1]+D[i-2])

然後預處理階乘 , 查詢時逆元搞一波就好了 (這年頭用ex_gcd寫逆元的真少) 


#include<bits/stdc++.h>
#define N 1000000
#define LL long long
#define P 1000000007
using namespace std;
LL C[N+50],D[N+50];
int T,n,m; LL x,y;
void exgcd(int a,int b){
	if(!b){x=1,y=0; return;}
	exgcd(b,a%b);
	LL x1=y,y1=x-a/b*y;
	x=x1,y=y1;
}
LL inv(int X){
	int a=X,b=P; exgcd(a,b);
	return (x+P)%P;
}
LL Ask(int x,int y){
	return C[x] * inv(C[x-y]) % P * inv(C[y]) % P;
}
int main(){
	C[0]=1 , D[0]=D[2]=1 , D[3]=2;
	for(int i=1;i<=N;i++) C[i] = (LL)C[i-1] * i % P;
	for(int i=4;i<=N;i++) D[i] = (LL)(i-1) * (D[i-1] + D[i-2]) % P;
	scanf("%d",&T); while(T--){
		scanf("%d%d",&n,&m);
		printf("%lld\n",Ask(n,m) * D[n-m] % P);
	}return 0;
}