[Luogu] P4071 [SDOI2016]排列計數
阿新 • • 發佈:2020-11-13
Description
多組詢問,求有多少\(1\)到\(n\)的排列\(a\),滿足序列恰好有\(m\)個位置\(i\),使得\(a_i = i\)。
答案對\(10^9 + 7\)取模。
Solution
算是錯排的板子題了。
錯排,就是對於某個\(1\sim{n}\)的排列,滿足\(\forall{i\in[1,n]},a_i\ne{i}\)。
那麼這道題其實就是求\(\dbinom{n}{m}D(n-m)\)。
現在要解決如何求\(D(n)\),有兩種方法求:
首先是遞推:對於一個錯排\(D(n)\),不失一般性,考慮\(1\)這個數放在哪個位置。它肯定不能放在\(a_1\)
其次是容斥: 正整數\(1, 2, 3, …, n\)的全排列有 \(n!\) 種,其中第\(k\)位是\(k\)的排列有\((n-1)!\)種;當\(k\)分別取\(1, 2, 3, …, n\)時,共有\(\dbinom{n}{1}*(n-1)!\)
\(D(n)=\sum\limits_{i=0}^n(-1)^i\dbinom{n}{i}(n-i)!\)
Code
#include <bits/stdc++.h> using namespace std; #define ll long long const ll mod = 1e9 + 7; ll t, n, m, pw[1000005], inv[1000005], wp[1000005]; ll read() { ll x = 0; char ch = getchar(); while (ch < '0' || ch > '9') ch = getchar(); while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();} return x; } ll qpow(ll base, ll p) { ll res = 1; while (p) { if (p & 1) res = res * base % mod; base = base * base % mod; p >>= 1; } return res; } int main() { t = read(); wp[0] = 1; wp[1] = 0; wp[2] = 1; for (ll i = 3; i <= 1000000; i ++ ) wp[i] = (i - 1) * ((wp[i - 1] % mod) + (wp[i - 2] % mod)) % mod; pw[1] = 1; for (ll i = 2; i <= 1000001; i ++ ) pw[i] = pw[i - 1] * i % mod; inv[1000001] = qpow(pw[1000001], mod - 2); for (ll i = 1000000; i >= 0; i -- ) inv[i] = inv[i + 1] * (i + 1) % mod; while (t -- ) { n = read(); m = read(); printf("%lld\n", (((pw[n] * inv[m]) % mod) * inv[n - m] % mod) * wp[n - m] % mod); } return 0; }