1969. 数组元素的最小非零乘积

给你一个正整数 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,就可以得到最后的乘积为(2^{p}-1)*(2^{p}-2)^{2^{p-1}-1}

现在问题转化为求(2^{p}-1)*(2^{p}-2)^{2^{p-1}-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;
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值