P4071 [SDOI2016]排列計數

P4071

題意:

給出一個序列,而後\(A[i]\)的位置放i則稱這是穩定的,而後剩下的\(n-m\)種則是不穩定的,c++

思路:

穩定的那\(m\)個數就是在\(n\)個數中選擇\(m\)個數讓他穩定,
而後剩下的不穩定的就是\(n-m\)個數作錯排的方案數,git

啥是錯排:

錯排例題ui

這就是一個錯排的板子題,而後咱們按照這個板子題來說錯排的原理,spa

就是\(n\)封信,裝到\(n\)個信封中,都裝錯了.code

咱們先看第一我的,由於第一我的裝錯的話有\(n-1\)種狀況,就長這樣:blog

而後拿出其中的一種來看,若是第二封信選擇了第一個信封那就是後邊的\(n-2\)個數作錯排:get

若是第二個沒有選第一封信,那就至關於對剩下的\(n-1\)個作錯排.it

咱們就很容易獲得一個遞推式(設\(f[i]\) 爲給i個數作錯排的方案數):ast

\[(n - 1) \ast (f[n - 1] + f[n - 2])\]class

邊界條件就是\(n\)\(1\)\(2\)的時候,當\(n\)只有\(1\)的時候只能放到這個信封中,因此方案數爲\(0\)

\(n\)\(2\)的時候只有下圖這一種可能,因此方案數爲\(1\).剩下的遞推可得.

求組合數的話咱們能夠用\(Lucas\)定理來求得.
\(Lucas\)定理:
\[Lucas(n, m) \% mod = \tbinom{n \% mod}{m \% mod} * Lucas(n, m)\]

由於這個題中\(n,m\)太大了,咱們能夠用公式直接求,由於須要\(mod\)一個數,

而後由於公式爲\(\frac {n!} {m!(n - m)!}\) 有除法,由於\(mod\)意義下沒有除法運算,

而後咱們能夠求出\(m!(n - m)!\) 的逆元,讓\(n!\) 乘以\(m!(n - m)!\) 的逆元來求\(mod\)意義下的組合數.

由於\(mod\)的這個數爲質數,咱們能夠直接利用費馬小定理來求逆元.

費馬小定理;
\[a^{p - 1} \equiv 1 \ (mod \ p)\]

因此 \[a \ast a^{p -2} \equiv 1 \ (mod \ p)\]
因此在$mod  p \(的狀況下\)a\(的逆元就是\)a ^ {p - 2}$

作的時候咱們能夠先處理出前\(1000000\)的錯排和階乘.

code :

#include <bits/stdc++.h>
#define N 1000010
#define M 1010
#define ll long long

using namespace std;
ll mod = 1e9 + 7;
ll t, n, m, f[N], jc[N];

ll read() {
    ll s = 0, f = 0;
    char ch = getchar();
    while (!isdigit(ch)) f |= (ch == '-'), ch = getchar();
    while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
    return f ? -s : s;
}

ll q_pow(ll a, ll b) {
    ll ans = 1;
    while (b) {
        if (b & 1) ans = (ans * a) % mod;
        a = (a * a) % mod;
        b >>= 1;
    }
    return ans;
}

ll c(ll a, ll b) {
    return (jc[a] % mod * q_pow(jc[b] * jc[a - b] % mod, mod - 2) % mod) % mod;
}

ll lucas(ll a, ll b) {
    if (!b) return 1;
    else return (lucas(a / mod, b / mod) * c(a % mod, b % mod)) % mod;
}

int main() {
    t = read();
    f[1] = 0, f[2] = 1, jc[1] = 1, jc[2] = 2;
    for (ll i = 3; i <= 1000000; i++)
        f[i] = ((i - 1) * (f[i - 1] + f[i - 2]) % mod) % mod, jc[i] = (jc[i - 1] * i) % mod;
    while (t--) {
        n = read(), m = read();
        if (n == m) puts("1");
        else if (n - m == 1) printf("0\n");
        else if (m == 0) printf("%lld\n",f[n]);
        else {
            ll ans = (f[n - m] % mod * lucas(n, m)) % mod;
            printf("%lld\n", ans);
        }
    }
    return 0;
}
相關文章
相關標籤/搜索