[BZOJ4517][Sdoi2016]排列计数(错排+组合数)

题目描述

传送门

题解

首先i个数的错排递推式为:f[n]=(n-1)*(f[n-1]+f[n-2])
可以理解为将n个信装到n个信封里,全部装错。
那么:1、将信1装在信封k里2、若①将信k装在信封1里,那么方案数为f[n-2]②将信k不装在信封1里,那么这时方案数为f[n-1]3、k有n-1种取值
那么这道题的答案即为 Cmnf[nm]
求C的时候预处理阶乘然后求逆元。

代码

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define LL long long
const int N=1e6;
const LL Mod=1e9+7;
int T;
LL n,m,ans;
LL f[N+5],mul[N+5];
inline void get_f(){
    f[0]=1; f[1]=0;
    for (int i=2;i<=N;++i) f[i]=((i-1)*(f[i-1]+f[i-2])%Mod)%Mod;
}
inline void get_mul(){
    mul[0]=1;
    for (int i=1;i<=N;++i) mul[i]=mul[i-1]*i%Mod;
}
inline LL fast_pow(LL a,LL p){
    LL ans=1;
    for (;p;p>>=1,a=a*a%Mod)
      if (p&1)
        ans=ans*a%Mod;
    return ans;
}
inline LL inv(LL a){return fast_pow(a,Mod-2);}
inline LL get_C(LL n,LL m){
    LL x=mul[n]%Mod;
    LL y=mul[m]*mul[n-m]%Mod;
    LL ans=x*inv(y)%Mod;
    return ans;
} 
int main(){
    get_f();
    get_mul();
    scanf("%d",&T);
    while (T--){
        scanf("%I64d%I64d",&n,&m);
        ans=get_C(n,m)*f[n-m]%Mod;
        printf("%lld\n",ans);
    }
}

你可能感兴趣的:(数论,bzoj,SDOI)