快速幂和求组合数

  1. 预备知识快速幂

计算

\[a^kmod\ p \]

快速幂就是快速算底数的k次幂,时间复杂度为O(logk)与朴素算法相比效率极大提升。

原理

k转化成二进制数

11的二进制数为1011

则十进制11写成

\[11=1\times2^3+0\times2^2+1\times2^1+1\times2^0 \]

k=11时,

\(a^k=a^{11}\)写成

\[a^{2^0}\times a^{2^1}\times a^{2^3} \]

代码实现:

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

求组合数

  1. 定义法

\[C_{n}^{m}=\cfrac {n\times(n-1)..\times(n-m+1)}{m!}=\cfrac{n!}{m!(n-m)!}=\prod_{i=1}^{m}\cfrac{n-m+i}{i} \]

int comb01(int n, int m) {
    long res = 1;
    for(int i=1; i <= m; i++) {
        res = res * (n-m+i) / i;
    }
    return (int) res;
}

2.性质法

\[C_n^m=C_{n-1}^m+C_{n-1}^{m-1} \]

一个物品有两种选择,要么选要么不选,n个中选m个的方案数(\(C_n^m\))等于当前一个物品不选的方案数(\(C_{n-1}^{m}\))和选当前物品的方案数(\(C_{n-1}^{m-1}\))的和。

int comb02(int n, int m, int p) {
    int[][] c = new int[n+1][m+1];
    for(int i=0; i <= n; i++) {
        for(int j=0; j <= i && j <= m; j++) {
            if(j==0) c[i][j] = 1;
            else c[i][j] = (c[i-1][j-1] + c[i-1][j]) % p;
        }
    }
    return c[n][m];
}

3.预处理逆元法

预处理出阶乘取模的余数fact[N],和阶乘取模的逆元infact[N]

如果取模的数是质数,可以使用费马小定理求逆元

费马小定理:若p为素数,则有\(a^{p-1}\equiv1\ (\mod p)\)

即:

\[a\times a^{p-2} \equiv 1 (\mod p) \]

\(a^{p-2}\)就是a在\(\mod p\)意义下的逆元。

如果多次计算

long[] fact, infact;
int N;
int p = 1000000007;//必须为素数
void init(int n, int p){
    N = 1001;// 必须N大于恒大于n
    fact = new long[N];
    infact = new long[N];
    fact[0] = infact[0] = 1;
    for(int i=1; i < N; i++) {
        fact[i] = fact[i-1]*i % p;
        infact[i] = qmi((int)fact[i], p-2, p);
    }
    //System.out.println(Arrays.toString(fact));
    //System.out.println(Arrays.toString(infact));
}
int comb03(int n, int m, int p) {
    init(p);
    long res = fact[n] * infact[n-m] % p * infact[m] % p;
    return (int)res;
}

或者计算一次

int p = 1000000007;//必须为素数
long ans1 = 1L, ans2 = 1L;//组合数的分子和分母
for(int i=n; i >= n-m+1; i--)
    ans1 = ans1 * i % p;
for(int i=1; i <= s; i++)
    ans2 = ans2 * i % p; 
ans2 = qmi((int)ans2, p-2, p);
int comb = ans1*ans2%p;
posted @ 2020-05-18 13:51  li修远  阅读(592)  评论(0编辑  收藏  举报