[组合 错排] BZOJ 4517 [Sdoi2016]排列计数

n个里选m个 然后n-m个里错排

***

错排相关公式

Dn=(n-1)*(Dn-1+Dn-2) D1=0,D2=1

Dn=n*Dn-1+(-1)^(n-2)=n*Dn-1+(-1)^n

Dn=n!(1-1/1!+1/2!-1/3!+……±1/n!)


#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define P 1000000007
using namespace std;
typedef long long ll;
 
inline char nc()
{
    static char buf[100000],*p1=buf,*p2=buf;
    if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
    return *p1++;
}
 
inline void read (ll &x){
    char c=nc(),b=1;
    for (;!(c>='0' && c<='9');c=nc()) if (c=='-') c=-1;
    for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
 
const int N=1000000;
ll D[N+5];
ll fac[N+5],inv[N+5];
 
inline void Pre()
{
    D[0]=1; D[1]=0; D[2]=1;
    for (int i=3;i<=N;i++)
        D[i]=(D[i-1]+D[i-2])%P*(i-1)%P;
    fac[0]=1;
    for (int i=1;i<=N;i++)
        (fac[i]=fac[i-1]*i)%=P;
    inv[1]=1;
    for (int i=2;i<=N;i++)
        (inv[i]=(P-P/i)*inv[P%i])%=P;
    inv[0]=1;
    for (int i=1;i<=N;i++)
        (inv[i]*=inv[i-1])%=P;
}
 
inline ll C(ll n,ll m){
    return fac[n]*inv[m]%P*inv[n-m]%P;
}
 
int main()
{
	freopen("t.in","r",stdin);
	freopen("t.out","w",stdout);
    ll Q,n,m,ans;
    Pre();
    read(Q); 
    while (Q--)
    {
        read(n); read(m);
        if (n<m) printf("0\n");
        ans=C(n,m)*D[n-m]%P;
        printf("%lld\n",ans);
    }
    return 0;
}


你可能感兴趣的:([组合 错排] BZOJ 4517 [Sdoi2016]排列计数)