组合数学

组合数学

方法一:预处理 + 递推

(C_a^b = C_{a-1}^b + C_{a-1}^{b-1})

时间复杂度:O((n^2))

#include <bits/stdc++.h>
using namespace std;

const int MOD = 1e9 + 7;
const char nl = '
';
const int N = 2000 + 50;

int c[N][N];

void init(){
    for (int i = 0; i < N; ++i)
        for (int j = 0; j <= i ; ++j)
            if (!j) c[i][j] = 1;
            else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % MOD;
}

int main(){
    ios::sync_with_stdio(false);
//    cin.tie(nullptr);

    init();

    int a, b;
    while (cin >> a >> b){
        cout << c[a][b] << nl;
    }
    return 0;
}

方法二:预处理 + 快速幂求逆元

(C_a^b = frac{a!}{b!(a-b)!})

时间复杂度:O((nlog{n}))

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int MOD = 1e9 + 7;
const char nl = '
';
const int N = 1e5 + 50;

int fact[N], infact[N];

int qmi(int a, int k, int p){
    int res = 1;
    while (k){
        if (k & 1) res = (LL)res * a % p;
        a = (LL)a * a % p;
        k >>= 1;
    }
    return res;
}

int main(){
    ios::sync_with_stdio(false);
//    cin.tie(nullptr);

    fact[0] = infact[0] = 1;
    for (int i = 1; i < N; ++i){
        fact[i] = (LL)fact[i - 1] * i % MOD;
        infact[i] = (LL)infact[i - 1] * qmi(i, MOD - 2, MOD) % MOD;
    }

    int a, b;
    while (cin >> a >> b){
        cout << (LL)fact[a] * infact[b] % MOD * infact[a - b] % MOD << nl;
    }
    return 0;
}

方法三:卢卡斯定理

(C_a^b equiv C_{a mod p}^{b mod p} cdot C_{a/p}^{b/p} pmod {p})

时间复杂度:O((plog{p}))

https://www.luogu.com.cn/problem/P3807

题意:求 (C_a^b mod p)

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const char nl = '
';
const int N = 1e5 + 50;

int p;

int qmi(int a, int k){
    int res = 1;
    while (k){
        if (k & 1) res = (LL)res * a % p;
        a = (LL)a * a % p;
        k >>= 1;
    }
    return res;
}

int C(int a, int b){
    int res = 1;
    for (int i = 1, j = a; i <= b; ++i, --j){
        res = (LL)res * j % p;
        res = (LL)res * qmi(i, p - 2) % p;
    }
    return res;
}

int lucas(int a, int b){
    if (a < p && b < p) return C(a, b);
    else return (LL)C(a % p, b % p) * lucas(a / p, b / p) % p;
}

int main(){
    ios::sync_with_stdio(false);
//    cin.tie(nullptr);

    int t;
    cin >> t;
    while (t--){
        int a, b;
        cin >> b >> a >> p;
        a = a + b;
        cout << lucas(a, b) << nl;
    }
    return 0;
}

方法四:高精度 + 分解质因数

(C_a^b = frac{a!}{b!(a-b)!})

(n!) 里质因子 p 的个数:(lfloor frac{n}{p} floor cdot lfloor frac{n}{p^2} floor cdot ldots cdot lfloor frac{n}{p^k} floor)

#include <bits/stdc++.h>
using namespace std;

const char nl = '
';
const int N = 5000 + 50;

int primes[N], tot;
int sum[N];
bool st[N];

void get_primes(int n){
    for (int i = 2; i <= n; ++i){
        if (!st[i]) primes[tot++] = i;
        for (int j = 0; primes[j] <= n / i; ++j){
            st[primes[j] * i] = true;
            if (i % primes[j] == 0) break;
        }
    }
}

int get(int n, int p){
    int res = 0;
    while (n){
        res += n / p;
        n /= p;
    }
    return res;
}

vector<int> mul(vector<int> A, int b){
    vector<int> C;
    int t = 0;
    for (int i = 0; i < A.size(); ++i){
        t += A[i] * b;
        C.push_back(t % 10);
        t /= 10;
    }
    while (t){
        C.push_back(t % 10);
        t /= 10;
    }
    return C;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int a, b;
    cin >> a >> b;

    get_primes(a);

    for (int i = 0; i < tot; ++i){
        int p = primes[i];
        sum[i] = get(a, p) - get(b, p) - get(a - b, p);
    }

    vector<int> res = {1};
    for (int i = 0; i < tot; ++i)
        for (int j = 0; j < sum[i]; ++j)
            res = mul(res, primes[i]);

    for (int i = res.size() - 1; i >= 0; --i) cout << res[i];
    cout << nl;

    return 0;
}

原文地址:https://www.cnblogs.com/xiaoran991/p/14402814.html