P4071 [SDOI2016]排列計數 錯排+組合數
阿新 • • 發佈:2020-09-17
題意:
求有多少種 \(1\) 到 \(n\) 的排列 \(a\),滿足序列恰好有 \(m\) 個位置 ii,使得 \(a_i=i\),\(T\)組詢問
答案對\(10^9+7\) 取模。
範圍&性質:\(1\le T\le 10^5,1\le n,m\le10^6\)
分析:
前置芝士:
錯排的常見公式:
記 \(f_i\)表示 \(i\) 階排列的個數,\(g_i\) 表示 \(i\) 階錯排的個數。
\(n! = f_n =n \sum C_n^igi\)
於是可以解得
\(g_i =n \sum (−1)^{n−i}C_n^i\ fi =n \sum (−1)^{n−i}C_n^i i!
= n!\sum_{i=0}^n \frac{(-1)^i}{i!}\)
由此還可以順便求出它的遞推式是:
\(g_{n+1}= (n + 1)g_n + (−1)^{n+1}\) 其初值是 \(g_0 = 1\)
對於這道題來說,先組合數從n個數中選擇m個,再乘上錯排
程式碼:
#include<bits/stdc++.h> using namespace std; namespace zzc { const int mod = 1e9+7; const int maxn = 1e6+5; int t,n,m; long long inv[maxn],fac[maxn],per[maxn]; void init() { inv[1]=1; fac[1]=1; fac[0]=1; for(int i=2;i<=1000000;i++) { inv[i]=(mod-mod/i)*inv[mod%i]%mod; fac[i]=fac[i-1]*i%mod; } inv[0]=1; for(int i=1;i<=1000000;i++) { inv[i]=inv[i]*inv[i-1]%mod; } per[0]=1; for(int i=1;i<=1000000;i++) { per[i]=(per[i-1]*i%mod+(i%2==1?-1:1))%mod; } } long long C(int a,int b) { return fac[a]*inv[a-b]%mod*inv[b]%mod; } void work() { init(); scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); printf("%lld\n",C(n,m)*per[n-m]%mod); } } } int main() { zzc::work(); return 0; }