【BZOJ】4517 [Sdoi2016]排列计数(数学+错排公式)

题目

传送门:QWQ

 

 

分析

$ O(nlogn) $预处理出阶乘和阶乘的逆元,然后求组合数就成了$O(1)$了。

最后再套上错排公式:$ \huge d[i]=(i-1) \times (d[i-1] + d[i-2])$其中$ d[i] $表示把i个数错排的方式数量,其中$d[1]=0,d[2]=1$

 

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD = 1e9+7;
const int maxn = 1100000;
ll inv[maxn+10], jc[maxn+10], jc_inv[maxn+10], d[maxn+10];
void exgcd(ll a,ll b,ll& x,ll& y) {
    if(!b) {x=1;y=0;return;}
    exgcd(b,a%b,y,x); y-=x*(a/b);
}
ll inverse(ll a) {
    ll x,y;
    exgcd(a,MOD,x,y);
    x = (x+MOD)%MOD;
    return x;
}
void init() {
    jc[0]=1; d[2]=1; d[0]=1; jc_inv[0]=1;
    for(int i=1;i<maxn;i++) {
        jc[i] = (jc[i-1]*ll(i)) % MOD;
        jc_inv[i] = inverse(jc[i]);
        if(i>2) d[i] = ((i-1) *(d[i-1] + d[i-2])) % MOD;
    }
}

ll C(ll n, ll m) {
    return jc[n] * jc_inv[m] % MOD * jc_inv[n-m] % MOD;
}
int main() {
    init();
    int t; scanf("%d",&t);
    while(t--) {
        ll n, m; scanf("%lld%lld",&n,&m);
        ll ans = C(n,m) * d[n-m] % MOD;
        printf("%lld\n",ans);
    }
}

 

 

 

posted @ 2018-10-03 23:03  noble_(noblex)  阅读(239)  评论(0编辑  收藏  举报
/* */