组合数的计算以及组合数对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];
        }
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值