算法 BISHI70 【模板】组合数 思路 求解代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 // 定义模数常量,用于所有取模运算 private static final int MOD = 1000000007; // 定义最大计算范围常量 private static final int MAX_N = 500001; // 存储阶乘值的数组 private static long[] fact = new long[MAX_N]; // 存储阶乘逆元的数组 private static long[] invFact = new long[MAX_N]; /** * 主方法 * 处理输入输出,调用预计算和组合数计算方法 * @param args 命令行参数 * @throws IOException 可能抛出的IO异常 */ public static void main(String[] args) throws IOException { // 使用BufferedReader读取输入,PrintWriter输出结果 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); // 预计算阶乘和逆元 precompute(); // 读取测试用例数量 int T = Integer.parseInt(br.readLine().trim()); // 处理每个测试用例 for (int i = 0; i < T; i++) { // 读取每行输入并分割 String[] str = br.readLine().split("\\s+"); int n = Integer.parseInt(str[0]); int m = Integer.parseInt(str[1]); // 输出组合数计算结果 out.println(nCr(m, n)); } // 清空输出缓冲区并关闭资源 out.flush(); out.close(); br.close(); } /** * 使用快速幂算法计算base的exp次方对MOD取模的结果 * 快速幂算法是一种高效计算幂运算的算法,时间复杂度为O(log n) * * @param base 底数 * @param exp 指数 * @return base的exp次方对MOD取模的结果 */ private static long mypower(long base, long exp) { // 初始化结果为1对MOD取模,防止MOD为1时结果错误 long ans = 1 % MOD; // 当指数exp大于0时继续循环 while (exp > 0) { // 如果当前指数是奇数,将base乘入结果 if (exp % 2 == 1) { ans = (ans * base) % MOD; } // base平方,指数减半 base = (base * base) % MOD; exp /= 2; } // 返回最终结果 return ans; } /** * 预计算阶乘和阶乘的逆元数组 * 使用动态规划方法计算阶乘数组fact[] * 使用费马小定理计算逆元数组invFact[] */ private static void precompute() { // 初始化0的阶乘和0的阶乘逆元为1 fact[0] = 1; invFact[0] = 1; // 动态规划计算阶乘数组 for (int i = 1; i < MAX_N; i++) { fact[i] = (fact[i - 1] * i) % MOD; } // 使用费马小定理计算最大阶乘的逆元 invFact[MAX_N - 1] = mypower(fact[MAX_N - 1], MOD - 2); // 逆向递推计算其他阶乘的逆元 for (int i = MAX_N - 2; i >= 1; i--) { invFact[i] = (invFact[i + 1] * (i + 1)) % MOD; } } /** * 计算组合数 nCr,即从n个物品中选取r个物品的组合数 * 使用预计算阶乘和阶乘逆元的方法来高效计算组合数 * * @param n 总物品数 * @param r 选取物品数 * @return 组合数结果,对MOD取模 */ private static long nCr(int n, int r) { // 如果r小于0或大于n,组合数为0 if (r < 0 || r > n) { return 0; } // 使用公式 nCr = n! / (r! * (n-r)!) 计算 // 通过预计算的阶乘数组fact和阶乘逆元数组invFact来计算 // 并对MOD取模,防止数值溢出 return (((fact[n] * invFact[r]) % MOD) * invFact[n - r]) % MOD; }