JavaInterview JavaInterview
首页
指南
分类
标签
归档
  • CSDN (opens new window)
  • 文档集合 (opens new window)
  • 系统架构 (opens new window)
  • 微信号 (opens new window)
  • 公众号 (opens new window)

『Java面试+Java学习』
首页
指南
分类
标签
归档
  • CSDN (opens new window)
  • 文档集合 (opens new window)
  • 系统架构 (opens new window)
  • 微信号 (opens new window)
  • 公众号 (opens new window)
  • 指南
  • 简历

  • Java

  • 面试

  • 算法

  • algorithm
  • leetcode
JavaInterview.cn
2025-08-11
目录

1735. 生成乘积数组的方案数Java

文章发布较早,内容可能过时,阅读注意甄别。

# 题目

给你一个二维整数数组 queries ,其中 queries[i] = [ni, ki] 。第 i 个查询 queries[i] 要求构造长度为 ni 、每个元素都是正整数的数组,且满足所有元素的乘积为 ki ,请你找出有多少种可行的方案。由于答案可能会很大,方案数需要对 109 + 7 取余 。

请你返回一个整数数组 answer,满足 answer.length == queries.length ,其中 answer[i]是第 i 个查询的结果。

示例 1:

输入:queries = [[2,6],[5,1],[73,660]]
输出:[4,1,50734910]
解释:每个查询之间彼此独立。
[2,6]:总共有 4 种方案得到长度为 2 且乘积为 6 的数组:[1,6],[2,3],[3,2],[6,1]。
[5,1]:总共有 1 种方案得到长度为 5 且乘积为 1 的数组:[1,1,1,1,1]。
[73,660]:总共有 1050734917 种方案得到长度为 73 且乘积为 660 的数组。1050734917 对 109 + 7 取余得到 50734910 。

示例 2 :

输入:queries = [[1,1],[2,2],[3,3],[4,4],[5,5]]
输出:[1,2,3,10,5]

提示:

  • 1 <= queries.length <= 104
  • 1 <= ni, ki <= 104

# 思路

排列组合 + 记忆化 + 模逆元。我的想法是首先求和不含1的因数的排列有多少种,比如16,它含有2个因数的排列有3种([2, 8],[8, 2], [3, 3])。再看题目要求生成的数组有多长,假设为10,那么对于长度2的排列而言,我们可以往其中插入8个1,其实就也是3 * C(10, 2)。按照这种思维,我们只需要通过递归不断求出每个数,不含1的每种长度的排列有多种即可。由于k小于10000且因数不含1,那么递归的过程其实是很快的(2^15 = 16384)。另外需要很多的记忆化操作,防止大量的重复计算而超时。 最后一个重点是模逆元,这个是用于求大数的排列组合的。

# 解法

class Solution {
    private static int mod = 1_000_000_007;

    // 记忆化
    private static long[] mods = new long[20];
    static {
        for(int i = 0; i < mods.length; i++) {
            mods[i] = modReverse(i);
        }
    }
    public int[] waysToFillArray(int[][] queries) {
        int[] ans = new int[queries.length];
        Set<Integer>[] sets = new Set[10001];
        for(int i = 0; i < sets.length; i++) {
            sets[i] = new HashSet<>();
        }
        int[] cn = new int[15];
        int n, k;
        for(int i = 0; i < queries.length; i++) {
            n = queries[i][0];
            k = queries[i][1];
            if(k == 1) {
                ans[i] = 1;
                continue;
            }
            dfs(n, k, sets, cn, 0);
            for(int j = 0; j < cn.length; j++) {
                // 这里用comine(n, j) 而不是combine(n, n - j), 这是因为j不超过15,当n较大时,计算速度快得多
                // 这也提示我们不放过任何优化的细节,思考清楚每一个细节。
                ans[i] += combine(n, j) * cn[j] % mod;
                ans[i] = ans[i] % mod;
                cn[j] = 0;
            }
        }
        return ans;
    }


    // 求某个n的因数集(不含1)
    private Set<Integer> decompose(int n) {
        Set<Integer> s = new HashSet<>();
        for(int i = 2; i * i <= n + 1; i++) {
            if(n % i == 0) {
                s.add(i);
                s.add(n / i);
            }
        }
        s.add(n);
        return s;
    }

    // 求不含1的因子的组合数
    private void dfs(int n, int k, Set<Integer>[] sets, int[] cn, int cur) {
        if(k == 1) {
            cn[cur]++;
            return;
        }
        if(sets[k].size() == 0) {
            sets[k] = decompose(k);
        }
        for(int m : sets[k]) {
            if(n >= 1) {
                dfs(n - 1, k / m, sets, cn, cur + 1);
            }
        }
    }

    // 求组合数(这个和下面的模逆元函数基本上都是固定写法)
    private long combine(int m, int n) {
        long ans = 1L;
        for(int i = 1; i <= n; i++) {
            ans = ans * (m - i + 1) % mod * mods[i] % mod;
        }
        return ans;
    }

    // 求模逆元,主要是解决大数的排列组合问题
    private static long modReverse(int n) {
        long ans = 1L, m = mod - 2, p = n;
        while(m > 0) {
            if((m & 1) == 1) {
                ans = ans * p % mod;
            }
            p = p * p % mod;
            m = m >> 1;
        }
        return ans;
    }
}

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

# 总结

  • 分析出几种情况,然后分别对各个情况实现
微信 支付宝
#Java
最近更新
01
1687. 从仓库到码头运输箱子 Java
08-11
02
1686. 石子游戏VI Java
08-11
03
1688. 比赛中的配对次数 Java
08-11
更多文章>
Theme by Vdoing | Copyright © 2019-2025 JavaInterview.cn
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式