码迷,mamicode.com
首页 > 其他好文 > 详细

组合数学

时间:2021-02-17 14:54:06      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:lse   复杂度   code   ++i   out   处理   char   求逆   init   

组合数学

方法一:预处理 + 递推

\(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 = ‘\n‘;
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(\(n\log{n}\))

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

typedef long long LL;

const int MOD = 1e9 + 7;
const char nl = ‘\n‘;
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 \bmod p}^{b \bmod p} \cdot C_{a/p}^{b/p} \pmod {p}\)

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

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

题意:求 \(C_a^b \bmod p\)

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

typedef long long LL;

const char nl = ‘\n‘;
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;
}

组合数学

标签:lse   复杂度   code   ++i   out   处理   char   求逆   init   

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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!