• 快速幂和求组合数


    1. 预备知识快速幂

    计算

    [a^kmod p ]

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

    原理

    k转化成二进制数

    11的二进制数为1011

    则十进制11写成

    [11=1 imes2^3+0 imes2^2+1 imes2^1+1 imes2^0 ]

    k=11时,

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

    [a^{2^0} imes a^{2^1} imes 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 imes(n-1).. imes(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 imes 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;
    
  • 相关阅读:
    Robot Framework的日期处理
    让IIS7和IIS6识别PUT和DELETE请求
    《微服务设计》读书笔记大纲
    规模化微服务——《微服务设计》读书笔记
    Uva 11396 爪分解
    Uva 10765 鸽子和炸弹
    Gym
    Gym
    Gym 101334C 无向仙人掌
    汇编实验十:编写子程序
  • 原文地址:https://www.cnblogs.com/lixyuan/p/12909275.html
Copyright © 2020-2023  润新知