给你一个正整数 p
。你有一个下标从 1 开始的数组 nums
,这个数组包含范围 [1, 2p - 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 已经是最小值。
示例 3:
输入:p = 3 输出:1512 解释:nums = [001, 010, 011, 100, 101, 110, 111] - 第一次操作中,我们交换第二个和第五个元素最左边的数位。 - 结果数组为 [001, 110, 011, 100, 001, 110, 111] 。 - 第二次操作中,我们交换第三个和第四个元素中间的数位。 - 结果数组为 [001, 110, 001, 110, 001, 110, 111] 。 数组乘积 1 * 6 * 1 * 6 * 1 * 6 * 7 = 1512 是最小乘积。
题解:这道题第一个问题,要了解这道题的机制是什么。我们任意交换两个数,让他们的乘积最小。例如,我们现在有a,b,c三个数,其中a<b<c,我们要使abc最小且不为零。我们要减少一个2的 幂数,增加一个数。
现在我们减少一个数,例如,(a-(a-1))*b*c = bc,当我们减少b-1时,ac=ac,当我们减少c-1时,ab=ab,可以看出我们当我们缩小最小值时,缩小了abc-bc,是最大的,所以我们缩小最小的。
现在我们增加一个数,例如,(a+c-1)*b*c=abc+bcc-bc,a(b+c-1)c = abc+acc-ac,ab(c+b-1) = abc+abb-ac,可以看出我们当我们增加最大值时,增加了abb-ac,是最小的,所以我们增大最大的。
那么,也就是用贪心的思想,把最大值增大最小值,最小值减为1,就可以得到最后的乘积为。
现在问题转化为求,很明显,这个用int肯定是过不了的,用long,首先直接用暴力求幂。
class Solution {
public static long myPower(int p, int mod) {
long a = 1;
for (long i = 0; i < ((1 << (p - 1))-1); i++) {
a %= mod;
a *= ((1 << p) - 2) % mod;
}
return a % mod;
}
public static int minNonZeroProduct(int p) {
int mod = 1000000007;
long a = ((1 << p) - 1) % mod;
return (int) (a*myPower(p, mod)%mod);
}
}
现在我们用快速幂,其思想就是将
根据上述思想 ,修改幂计算,就可以通过了。
class Solution {
public int minNonZeroProduct(int p) {
if (p == 1) {
return 1;
}
long mod = 1000000007;
long x = fastPow(2, p, mod) - 1;
long y = (long) 1 << (p - 1);
return (int) (fastPow(x - 1, y - 1, mod) * x % mod);
}
public long fastPow(long x, long n, long mod) {
long res = 1;
for (; n != 0; n >>= 1) {
if ((n & 1) != 0) {
res = res * x % mod;
}
x = x * x % mod;
}
return res;
}
}