- [Leetcode 3145. Find Products of Elements of Big Array](#Leetcode 3145. Find Products of Elements of Big Array)
- [1. 解题思路](#1. 解题思路)
- [2. 代码实现](#2. 代码实现)
1. 解题思路
这道题思路上还是比较直接的,就是实现上非常的繁琐,着实花了不少力气。
显然,这道题本质上就是要实现一个快速的query(i, j, k)
函数,可以求出big array当中i到j位置当中所有元素的积对于k的模。
而这个,我们就可以拆分为以下一个问题:
- 定义函数
count(i)
来对于1到i个位置所有的二的指数出现的次数来进行计数。
此时,我们就可以快速得到query(i, j, k)
函数的伪代码实现如下:
python
def query(i, j, k):
cnt1 = count(i-1)
cnt2 = count(j)
ans = 1
for p, v in cnt2.items():
# p为2的指数
ans = ans * pow(p, v) % k
return 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代码实现如下:
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 1
elif k == 1:
return i % mod
return (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 = 0
for d in digits[::-1]:
num = num * 2 + d
return num
@lru_cache(None)
def count_power(n, k):
digits = list(num2digits(n))
if k >= len(digits):
return 0
if digits[k] == 0:
i = 0
while i < k:
digits[i] = 1
i += 1
while digits[i] == 0:
digits[i] = 1
i += 1
digits[i] = 0
digits.pop(k)
return digits2num(digits) + 1
@lru_cache(None)
def get_big_num_end_index(num):
ans = 0
k = 0
while True:
cnt = count_power(num, k)
if cnt == 0:
break
ans += cnt
k += 1
return ans
@lru_cache(None)
def _count(idx, k):
i, j = 0, idx+1
while j-i > 1:
m = (i+j) // 2
cnt = get_big_num_end_index(m)
if cnt <= idx:
i = m
else:
j = m
loc = get_big_num_end_index(i)
r = idx-loc
digits_j = num2digits(j)
extra = 1 if len(digits_j) > k and digits_j[k] == 1 and sum(digits_j[:k+1]) <= r else 0
return count_power(i, k) + extra
@lru_cache(None)
def count(idx):
if idx == 0:
return defaultdict(int)
cnt = defaultdict(int)
k = 0
while True:
c = _count(idx, k)
if c == 0:
break
cnt[k] = c
k += 1
return cnt
@lru_cache(None)
def query(i, j, mod):
if i == 0:
cnt1 = defaultdict(int)
else:
cnt1 = count(i-1)
cnt2 = count(j)
ans = 1
p, k = 1, 0
while cnt2[k] > 0:
c = cnt2[k] - cnt1[k]
ans = (ans * power(p, c, mod)) % mod
p = (p * 2) % mod
k += 1
return ans
return [query(i+1, j+1, mod) for i, j, mod in queries]
提交代码评测得到:耗时7624ms,占用内存662.3MB。