- Leetcode 3145. Find Products of Elements of Big Array
- 1. 解题思路
- 2. 代码实现
- 题目链接:3145. Find Products of Elements of Big Array
1. 解题思路
这道题思路上还是比较直接的,就是实现上非常的繁琐,着实花了不少力气。
显然,这道题本质上就是要实现一个快速的query(i, j, k)
函数,可以求出big array当中i到j位置当中所有元素的积对于k的模。
而这个,我们就可以拆分为以下一个问题:
- 定义函数
count(i)
来对于1到i个位置所有的二的指数出现的次数来进行计数。
此时,我们就可以快速得到query(i, j, k)
函数的伪代码实现如下:
def query(i, j, k):cnt1 = count(i-1)cnt2 = count(j)ans = 1for p, v in cnt2.items():# p为2的指数ans = ans * pow(p, v) % kreturn ans
因此,我们的问题就有转换为了如何实现这个count函数。
对于这个问题,我们将其进一步拆分为以下两个问题:
- 坐标位置i对应的整数为多少?
- 对于任意整数n,将1到n拆分为big array之后其中包含的所有2的指数出现的次数是多少?
对于后者,我们将n写作二进制数,然后看从 2 p 2^p 2p一共出现了多少次就只需要找到不大于n的第一个p位置上为1的数,然后将第p个位置抽走,此时剩下的的位上表示的数字k加1就是一共出现了多少次(0-k)。比如5,写作二进制就是101
,此时对于1到5,出现的1的个数就是3(2(10)+1),出现的2的个数就是2(1(1)+1),出现的4的个数就是2(1(01)+1)。
这样,我们就可以很快地求得对任意整数n,从1到n当中出现过的2的指数的个数,此时,我们将其相加即可得到从1到n组成的big array的元素个数,因此,通过二分法,我们又可以快速回答1中的问题,即对于任意坐标i,它对应的整数是多少。
综上,我们就对上述问题进行了完整的回答。思路上还是比较简单清晰的,不过实现上……
诸君,自求多福吧,反正我是被折磨得够呛……
2. 代码实现
给出python代码实现如下:
class Solution:def findProductsOfElements(self, queries: List[List[int]]) -> List[int]:@lru_cache(None)def power(i, k, mod):if i==1 or k == 0:return 1elif k == 1:return i % modreturn (power(i, k//2, mod) * power(i, k-k//2, mod)) % mod@lru_cache(None)def num2digits(n):return tuple([int(x) for x in bin(n)[2:][::-1]])def digits2num(digits):num = 0for d in digits[::-1]:num = num * 2 + dreturn num@lru_cache(None)def count_power(n, k):digits = list(num2digits(n))if k >= len(digits):return 0if digits[k] == 0:i = 0while i < k:digits[i] = 1i += 1while digits[i] == 0:digits[i] = 1i += 1digits[i] = 0digits.pop(k)return digits2num(digits) + 1@lru_cache(None)def get_big_num_end_index(num):ans = 0k = 0while True:cnt = count_power(num, k)if cnt == 0:breakans += cntk += 1return ans@lru_cache(None)def _count(idx, k):i, j = 0, idx+1while j-i > 1:m = (i+j) // 2cnt = get_big_num_end_index(m)if cnt <= idx:i = melse:j = mloc = get_big_num_end_index(i)r = idx-locdigits_j = num2digits(j)extra = 1 if len(digits_j) > k and digits_j[k] == 1 and sum(digits_j[:k+1]) <= r else 0return count_power(i, k) + extra@lru_cache(None)def count(idx):if idx == 0:return defaultdict(int)cnt = defaultdict(int)k = 0while True:c = _count(idx, k)if c == 0:breakcnt[k] = ck += 1return cnt@lru_cache(None)def query(i, j, mod):if i == 0:cnt1 = defaultdict(int)else:cnt1 = count(i-1)cnt2 = count(j)ans = 1p, k = 1, 0while cnt2[k] > 0:c = cnt2[k] - cnt1[k]ans = (ans * power(p, c, mod)) % modp = (p * 2) % modk += 1return ansreturn [query(i+1, j+1, mod) for i, j, mod in queries]
提交代码评测得到:耗时7624ms,占用内存662.3MB。