【Python】 快速幂学习: Leetcode 1969. 数组元素的最小非零乘积

发布于:2024-03-21 ⋅ 阅读:(68) ⋅ 点赞:(0)

题目描述

给你一个正整数 p 。你有一个下标从 1 开始的数组 nums ,这个数组包含范围 [1, 2^p - 1] 内所有整数的二进制形式(两端都 包含)。你可以进行以下操作任意次:

···从 nums 中选择两个元素 x 和 y 。
···选择 x 中的一位与 y 对应位置的位交换。对应位置指的是两个整数 相同位置 的二进制位。

比方说,如果 x = 1101 且 y = 0011 ,交换右边数起第 2 位后,我们得到 x = 1111 和 y = 0001 。
请你算出进行以上操作 任意次 以后,nums 能得到的 最小非零 乘积。将乘积对 109 + 7 取余 后返回。
注意:答案应为取余 之前 的最小值。

示例 1:
输入:p = 1
输出:1
解释:nums = [1] 。
只有一个元素,所以乘积为该元素。

示例 2:
输入:p = 2
输出:6
解释:nums = [01, 10, 11] 。
所有交换要么使乘积变为 0 ,要么乘积与初始乘积相同。
所以,数组乘积 1 * 2 * 3 = 6 已经是最小值。

思路

首先注意到,因为仅可以交换二进制位,因此,这些元素的和恒定。现求这些元素的乘积,根据基本不等式,ab <= ((a+b) / 2)^2,当a=b时取最大值,当a和b的差越大,即|a-b|越大时,值越小。

那么,需要做的是,元素交换过程中,尽可能拉大他们的差距,尽可能获得更小或更大的元素。换句话来说,在乘积非零的最小的状态下,任意互换元素的两个二进制位,都不会使二者的差距变大。

同时,在2**(p-1)个二进制数中,最低位为1的数量为2**(p-1)个,次高位为1的数量也为2**(p-1)个,类推。

这样一来,可以想象,在2**p - 1个元素的数组上分配这些二进制位,按照以上推论堆放出最小非零乘积。
在这里插入图片描述
可以推出答案为:
在这里插入图片描述

代码

class Solution:
    def minNonZeroProduct(self, p: int) -> int:
        return (2**p-1)*((2**p-2)**(2**(p-1)-1))%(10**9 + 7)

但是以上代码会超时,需要优化幂算法,可以自己实现快速幂:

class Solution:
    def minNonZeroProduct(self, p: int) -> int:
        self.res = 10**9 + 7
        p_pow = 1 << p
        return (p_pow-1)*(self.binpow((p_pow-2)%self.res,(p_pow//2-1)))%self.res
    
    def binpow(self,a, b):
        res = 1
        while b > 0:
            if (b & 1):
                res = res * a
                res %= self.res
            a = a * a
            a %= self.res
            b >>= 1
        return res

注意以上,指数项p也可以取余。根据费马小定理,p可以对(10**9 + 7)-1取余(注意减一)

另一种方法就是用python内置的pow函数,其已做出优化:

class Solution:
    def minNonZeroProduct(self, p: int) -> int:
        mod = 10**9 + 7
        return (2**p - 1) * pow(2**p - 2, 2 ** (p - 1) - 1, mod) % mod
本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到