Leetcode 3145. Find Products of Elements of Big Array

  • [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。

相关推荐
吃瓜市民13 天前
剑指offer - 面试题11 旋转数组的最小数字
数据结构·c++·算法·二分法
斯内科20 天前
C#使用文件读写操作实现仙剑五前传称号存档修改
c#·二进制·修改器
Espresso Macchiato1 个月前
Leetcode 3449. Maximize the Minimum Game Score
贪婪算法·二分法·leetcode hard·leetcode 3449·leetcode周赛436
查士丁尼·绵1 个月前
笔试-二进制
二进制
Mysticbinary1 个月前
二进制与位操作
二进制·位操作
前端熊猫2 个月前
二进制、八进制、十进制和十六进制的相互转换
c语言·开发语言·二进制·十六进制·八进制
Espresso Macchiato3 个月前
Leetcode 3389. Minimum Operations to Make Character Frequencies Equal
动态规划·leetcode hard·分类讨论·leetcode 3389·leetcode周赛428
源代码•宸3 个月前
Leetcode—367. 有效的完全平方数【简单】
c++·经验分享·算法·leetcode·二分法
Espresso Macchiato3 个月前
Leetcode 3373. Maximize the Number of Target Nodes After Connecting Trees II
动态规划·leetcode hard·leetcode 3373·leetcode周赛426·树的遍历
zhuqiyua3 个月前
直接调用本地API(NTAPI)
操作系统·windbg·逆向·二进制·osed