组合数的计算以及组合数对p取余后结果的计算

前奏:统计 n! 中的所有质因子中pi的个数

普通方法:复杂度O(nlogn), 当n为10的18次方无法承受

// 复杂度O(nlogn), n为10的18次方无法承受
int cal(int n, int p){
    int ans = 0;
    for (int i = 2; i <= n; i++){
        int temp = i;
        while (temp % p == 0){
            ans++;
            temp /= p;            // temp除以p
        }
    }
    return ans;
}

改进后的方法:复杂度只有O(logn)

int cal(int n, int p){
    int ans = 0;
    while (n){
        ans += n / p;        // 累加 n / p^k
        n /= p;                // 相当于分母多乘一个 p
    }
    return ans;
}

1. 组合数的计算(如果只需计算一个组合数则用法三,如果计算很多个组合数则用法二

法一:通过定义计算

long long C(long long n, long long m){
    long long ans = 1;
    for (long long i = 1; i <= n; i++){
        ans *= i;
    }
    for (long long i = 1; i <= m; i++){
        ans /= i;
    }
    for (long long i = 1; i <= n - m; i++){
        ans /= i;
    }
    return ans;
}

法二通过递推式计算

int res[100][100];            // 用于存放组合数的值
// 法二:通过递推式计算
long long C(long long n, long long m){
    if (n == 0 || m == n)
        return 1;    
    if (res[n][m] != 0) 
        return res[n][m];
    return res[n][m] = C(n - 1, m) + C(n - 1, m - 1);    // 赋值给res[n][m]并返回
}

把所有组合数都计算出来

// 把所有组合数都计算出来
const int n = 60;
void calC(){
    for (int i = 0; i <= n; i++){
        res[i][0] = res[i][i] = 1;        // 初始化边界
    }
    for (int i = 2; i <= n; i++){
        for (int j = 0; j <= i / 2; j++){
            res[i][j] = res[i - 1][j] + res[i - 1][j - 1];    // 递推计算C(i, j)
            res[i][i - j] = res[i][j];        // C(i, i - j) = C(i, j)
        }
    }
}

法三:定义变形,复杂度O(m)(推荐)

long long C(long long n, long long m){
    long long ans = 1;
    for (int i = 1; i <= m; i++){
        ans = ans * (n - m + i) / i;        // 注意一定要先乘再除
    }
}

2. 计算C(n, m) % p

递归

// 递归
int res[1010][1010] = { 0 };
int C(int n, int m, int p){
    if (m == 0 || m == n) return 1;
    if (res[n][m] != 0) 
        return res[n][m];        // 已经有值
    return res[n][m] = (C(n - 1, m, p) + C(n - 1, m - 1, p)) % p;    // 赋值后返回
}

非递归

void CalC(int n, int p){
    for (int i = 0; i <= n; i++){
        res[i][0] = res[i][i] = 1;        // 初始化边界
    }
    for (int i = 2; i <= n; i++){
        for (int j = 0; j <= i; j++){
            res[i][j] = (res[i - 1][j] + res[i - 1][j - 1]) % p;    
            res[i][i - j] = res[i][j];
        }
    }
}

猜你喜欢

转载自www.cnblogs.com/hi3254014978/p/12233982.html