题目描述
给你一个正整数 p
。你有一个下标从 1 开始的数组 nums
,这个数组包含范围 [1, 2p - 1]
内所有整数的二进制形式(两端都 包含)。你可以进行以下操作 任意 次:
- 从
nums
中选择两个元素x
和y
。 - 选择
x
中的一位与y
对应位置的位交换。对应位置指的是两个整数 相同位置 的二进制位。
比方说,如果 x = 11_**0**_1
且 y = 00_**1**_1
,交换右边数起第 2
位后,我们得到 x = 11_**1**_1
和 y = 00_**0**_1
。
请你算出进行以上操作 任意次 以后,nums
能得到的 最小非零 乘积。将乘积对 109 + 7
取余 后返回。
**注意:**答案应为取余 之前 的最小值。
思路
参考官方题解,给定三个整数 且 ,将一个数缩小 1,另一个数增加 1,怎样缩小才能使缩小后的乘积最小?
- 如果缩小最小的元素,即 ,缩小了 ;
- 如果缩小最大的元素,即 ,缩小了 。
此时 ,因此缩小最小的元素是最优选。
同理,如果要为另一个数增加 1,增大最大的元素是最优选。
而本题中两个数进行相同的位交换时,本质上就是将一个元素缩小 ,另一个元素增大 。根据上面的推断,我们有一个朴素的贪心思路:优先缩小数组中最小的元素,再增加数组中最大的元素。
对于数组:
此时它的乘积为:
我们可以怎样贪心呢?尝试从左向右分析吧。
尝试将第 位的所有 1 与第 位的 进行交换,我们可以得到:
但是,因为我们要做乘积,因此左侧的 个数不能为 0,此时就可以将右侧 个数的最低位与前面的数字交换,最终得到:
结果就是 。由于幂数较大,可以通过快速幂简化运算。
最简单的 Python 代码为:
这份代码的时间复杂度是多少呢?参考这篇回答,pow
和 **
的时间复杂度都是 ,因此整体的时间复杂度是 。(好像不大对劲)