【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); } }