【Luogu P4071】[SDOI2016]排列計數
阿新 • • 發佈:2020-07-20
題目大意:
求有多少種 \(1\) 到 \(n\) 的排列 \(a\),滿足序列恰好有 \(m\) 個位置 \(i\),使得 \(a_i=i\),答案對 \(10^{9}+7\)。
正文:
可以先列個表:
再從題目意思出發,若 \(m=0\),即沒有一個數字在自己位置上,那就是錯位排列。錯位排列的遞推式是 \(f_i=(i-1)(f_{i-1}+f{i-2})\)。而我們在列表的過程中會發現,特殊情況除外,除在自己位置上的 \(m\) 個數,其它的數進行錯位排列。也就是說 最終答案 = 確定排列的數的總值 * 其它數錯位排列。
問題來了,確定排列的數的總值是多少?我們不妨舉舉例,設 \(n=4,m=2\)
程式碼:
ll _pow(ll a, int b){ a %= p; ll ans = 1; for(; b; b >>= 1, a = a * a % p) if(b & 1) ans = ans * a % p; return ans; } void init() { prod[0] = 1; for (register int i = 1; i <= 1000000; i++) prod[i] = (prod[i - 1] * i) % p, inv[i] = _pow(prod[i], p - 2); a[2] = 1; for (register int i = 3; i <= 1000000; i++) a[i] = (i - 1) * ((a[i - 1] + a[i - 2]) % p) % p; } int main() { int t; init(); for (scanf("%d", &t); t--;) { scanf("%d%d", &n, &m); if(m == 0) { printf("%lld\n", a[n]); continue; } if(n == m){puts("1");continue;} if(n - 1 == m){puts("0");continue;} //圖表中的幾個特殊情況 printf("%lld\n", (prod[n] * inv[m] % p * inv[n-m] % p) * a[n-m] % p); //括號內的是組合數,a陣列是錯位排列個數 } return 0; }