LeetCode 题解工作台

大数组元素的乘积

一个非负整数 x 的 强数组 指的是满足元素为 2 的幂且元素总和为 x 的最短有序数组。下表说明了如何确定 强数组 的示例。可以证明, x 对应的强数组是独一无二的。 数字 二进制表示 强数组 1 0000 1 [1] 8 0 1 000 [8] 10 0 1 0 1 0 [2, 8] 13 0 …

category

3

题型

code_blocks

4

代码语言

hub

3

相关题

当前训练重点

困难 · 二分·搜索·答案·空间

bolt

答案摘要

连续的正整数数字对应的强整数数组连接得到数组 ,题目需要我们求出对于每个查询 $[\textit{left}, \textit{right}, \textit{mod}]$,子数组 的乘积对 取模的结果。由于子数组每个元素都是 的幂,这等价于求子数组的幂次之和 ,然后计算 $2^{\textit{power}} \bmod \textit{mod}$。例如,对于子数组 $[1, 4, 8]$…

Interview AiBox logo

Interview AiBox 实时 AI 助手,陪你讲清 二分·搜索·答案·空间 题型思路

试试 AiBox 面试助手arrow_forward
description

题目描述

一个非负整数 x 的 强数组 指的是满足元素为 2 的幂且元素总和为 x 的最短有序数组。下表说明了如何确定 强数组 的示例。可以证明,x 对应的强数组是独一无二的。

数字 二进制表示 强数组
1 00001 [1]
8 01000 [8]
10 01010 [2, 8]
13 01101 [1, 4, 8]
23 10111 [1, 2, 4, 16]

 

我们将每一个升序的正整数 i (即1,2,3等等)的 强数组 连接得到数组 big_nums ,big_nums 开始部分为 [1, 2, 1, 2, 4, 1, 4, 2, 4, 1, 2, 4, 8, ...] 。

给你一个二维整数数组 queries ,其中 queries[i] = [fromi, toi, modi] ,你需要计算 (big_nums[fromi] * big_nums[fromi + 1] * ... * big_nums[toi]) % modi 。

请你返回一个整数数组 answer ,其中 answer[i] 是第 i 个查询的答案。

 

示例 1:

输入:queries = [[1,3,7]]

输出:[4]

解释:

只有一个查询。

big_nums[1..3] = [2,1,2] 。它们的乘积为 4。结果为 4 % 7 = 4

示例 2:

输入:queries = [[2,5,3],[7,7,4]]

输出:[2,2]

解释:

有两个查询。

第一个查询:big_nums[2..5] = [1,2,4,1] 。它们的乘积为 8 。结果为  8 % 3 = 2

第二个查询:big_nums[7] = 2 。结果为 2 % 4 = 2

 

提示:

  • 1 <= queries.length <= 500
  • queries[i].length == 3
  • 0 <= queries[i][0] <= queries[i][1] <= 1015
  • 1 <= queries[i][2] <= 105

 

lightbulb

解题思路

方法一:二分查找 + 位运算

连续的正整数数字对应的强整数数组连接得到数组 bignums\textit{bignums},题目需要我们求出对于每个查询 [left,right,mod][\textit{left}, \textit{right}, \textit{mod}],子数组 bignums[left..right]\textit{bignums}[\textit{left}..\textit{right}] 的乘积对 mod\textit{mod} 取模的结果。由于子数组每个元素都是 22 的幂,这等价于求子数组的幂次之和 power\textit{power},然后计算 2powermodmod2^{\textit{power}} \bmod \textit{mod}。例如,对于子数组 [1,4,8][1, 4, 8],即 [20,22,23][2^0, 2^2, 2^3],其幂次之和为 0+2+3=50 + 2 + 3 = 5,所以 25modmod2^5 \bmod \textit{mod} 就是我们要求的结果。

因此,我们不妨将 bignums\textit{bignums} 转换为幂次数组,即对于子数组 [1,4,8][1, 4, 8],我们将其转换为 [0,2,3][0, 2, 3]。这样,问题转换为求幂次数组的子数组之和,即 power=f(right+1)f(left)\textit{power} = \textit{f}(\textit{right} + 1) - \textit{f}(\textit{left}),其中 f(i)\textit{f}(i) 表示 bignums[0..i)\textit{bignums}[0..i) 的幂次之和,也即是前缀和。

接下来,就是根据下标 ii 计算 f(i)\textit{f}(i) 的值。我们可以使用二分查找的方法,先找到强数组长度和小于 ii 的最大数字,然后再计算剩下的数字的幂次之和。

我们根据题目描述,列出数字 0..140..14 的强整数:

nums\textit{nums} 8(232^3) 4(222^2) 2(212^1) (202^0)
0 0 0 0 0
1 0 0 0 1
2 0 0 1 0
3 0 0 1 1
4 0 1 0 0
5 0 1 0 1
6 0 1 1 0
7 0 1 1 1
8 1 0 0 0
9 1 0 0 1
10 1 0 1 0
11 1 0 1 1
12 1 1 0 0
13 1 1 0 1
14 1 1 1 0

将数字按照 [2i,2i+11][2^i, 2^{i+1}-1] 的区间划分为不同的颜色,可以发现,区间 [2i,2i+11][2^i, 2^{i+1}-1] 的数字,相当于在区间 [0,2i1][0, 2^i-1] 的数字基础上,每个数字加上 2i2^i。我们可以根据这个规律,计算出 bignums\textit{bignums} 的前 ii 组的所有数字的强数组个数之和 cnt[i]\textit{cnt}[i] 和幂次之和 s[i]\textit{s}[i]

接下来,对于任何数字,我们考虑如何计算其强数组的个数和幂次之和。我们可以通过二进制的方式,从最高位开始,诸位计算。例如,对于数字 13=23+22+2013 = 2^3 + 2^2 + 2^0,前 232^3 个数字的结果可以由 textitcnt[3]textit{cnt}[3]s[3]\textit{s}[3] 计算得到,而剩下的 [23,13][2^3, 13] 的结果,相当于给 [0,1323][0, 13-2^3] 的所有数字,即 [0,5][0, 5] 的所有数字的强数组增加 33,问题转换为计算 [0,5][0, 5] 的所有数字的强数组的个数和幂次之和。这样,我们可以计算出任意数字的强数组的个数和幂次之和。

最后,我们可以根据 power\textit{power} 的值,利用快速幂的方法,计算出 2powermodmod2^{\textit{power}} \bmod \textit{mod} 的结果。

时间复杂度 O(q×logM)O(q \times \log M),空间复杂度 (logM)(\log M)。其中 qq 为查询的个数,而 MM 为数字的上界,本题中 M1015M \le 10^{15}

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
m = 50
cnt = [0] * (m + 1)
s = [0] * (m + 1)
p = 1
for i in range(1, m + 1):
    cnt[i] = cnt[i - 1] * 2 + p
    s[i] = s[i - 1] * 2 + p * (i - 1)
    p *= 2


def num_idx_and_sum(x: int) -> tuple:
    idx = 0
    total_sum = 0
    while x:
        i = x.bit_length() - 1
        idx += cnt[i]
        total_sum += s[i]
        x -= 1 << i
        total_sum += (x + 1) * i
        idx += x + 1
    return (idx, total_sum)


def f(i: int) -> int:
    l, r = 0, 1 << m
    while l < r:
        mid = (l + r + 1) >> 1
        idx, _ = num_idx_and_sum(mid)
        if idx < i:
            l = mid
        else:
            r = mid - 1

    total_sum = 0
    idx, total_sum = num_idx_and_sum(l)
    i -= idx
    x = l + 1
    for _ in range(i):
        y = x & -x
        total_sum += y.bit_length() - 1
        x -= y
    return total_sum


class Solution:
    def findProductsOfElements(self, queries: List[List[int]]) -> List[int]:
        return [pow(2, f(right + 1) - f(left), mod) for left, right, mod in queries]
speed

复杂度分析

指标
时间complexity depends on the number of queries and the bit-length of numbers, typically O(queries * log(max_index) * log(max_bit)), while space complexity is O(log(max_index) * log(max_bit)) for precomputed counts.
空间Depends on the final approach
psychology

面试官常问的追问

外企场景
  • question_mark

    Look for an approach that avoids building the entire big_nums array explicitly.

  • question_mark

    Check if the candidate can compute bit contributions using O(log n) methods.

  • question_mark

    Verify understanding of modular arithmetic on large products.

warning

常见陷阱

外企场景
  • error

    Attempting to generate big_nums entirely leads to memory overflow.

  • error

    Failing to compute bit contributions correctly for large indices.

  • error

    Ignoring modular reduction during intermediate multiplication causes overflow.

swap_horiz

进阶变体

外企场景
  • arrow_right_alt

    Compute sums instead of products for big_nums subarrays using the same binary search strategy.

  • arrow_right_alt

    Handle queries where elements are filtered by additional conditions on powers of two.

  • arrow_right_alt

    Apply the same binary search over valid answer space for ranges in 2D matrices built from powerful arrays.

help

常见问题

外企场景

大数组元素的乘积题解:二分·搜索·答案·空间 | LeetCode #3145 困难