快速幂(技巧版)+ 一点点数学推理

首先还是简单说一下这个题目描述:
给你一个正整数 p 。你有一个下标从 1 开始的数组 nums ,这个数组包含范围 [1, 2^p - 1] 内所有整数的二进制形式(两端都 包含)。你可以进行以下操作 任意 次:
从 nums 中选择两个元素 x 和 y 。
选择 x 中的一位与 y 对应位置的位交换。对应位置指的是两个整数相同位置的二进制位。
比方说,如果 x = 1101 且 y = 0011 ,交换右边数起第 2 位后,我们得到 x = 1111 和 y = 0001 。

请你算出进行以上操作任意次以后,nums 能得到的最小非零乘积。将乘积对 10^9 + 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 是最小乘积。

注意:1 <= p <= 60
其实这题目大家乍一看可能不知道从何下手,但是我们一看那个样例输入,还有题目描述的例子,我们可以发现一个规律就是他的目标就是尽可能多的去构造这个1和另外一个数相乘(具体原理大家可以参考leetcode灵神题解数学证明,很多时候都是玄学直观感觉就是对的,可以大胆尝试着提交一下),而且我们可以看到最明显的就是p=3的时候的构造,我们可以发现一个非常明显的特征,那就是除了最后一个7(也就是 2^p - 1),其他的构造都是1*6(这个6也就是(2^p - 2) 其实如果这里大家还不是很确定这个规律,我们自己在本子上在看一下p=4的情况 我们来简单看一下 这里以0010和1101为例,这两个数字就类比我们上面的正数第二个数字和倒数第三个数字的交换构造,可以得到交换后的结果为 1110 和 0001也就是我们上面得到的 (2^p - 2 和 1)推理到这里大家就可以大胆猜想这题的答案其实就是若干个(2p-2)相乘和(2p-1)在相乘即可得到结果,那么至于这个若干个n是多少呢?
这里其实很好分析,大家看那个p=3的例子就可以看到,其实也就是去掉了最后一个数(2p-1)然后两两个数字构成一对去构造,所以这个n实际上就是(2p-2)/2对数字,也就是最终结果我们可以得到结果为: ( 2 p − 1 ) ⋅ ( 2 p − 2 ) 2 p − 1 − 1 (2^p-1)\cdot(2^p-2)^{2^{p-1}-1} (2p1)(2p2)2p11

这里由于幂次太高,所以需要快速幂poww(x,p)
这里大家可以修改一下快速幂的使用技巧,由于我们的幂次是(2^(p-1) - 1)也就是
p-1个1组成的二进制数字,所以我们可以省去判断 p&1的步骤,因为每一步都是1
例如3&1=1,1&1=1…得到最终的代码为:

class Solution {
public:
    const int mod = 1e9 + 7;
    typedef long long ll;
    long long poww(long long x ,int p){
        x %= mod;
        long long res = 1;
        while(p--){//p个1的二进制数求快速幂就可以这么做,当然你写正常版本的快速幂也没有问题
            res = res * x % mod;
            x = x * x % mod;
        }
        return res % mod;
    }
    int minNonZeroProduct(int p) {
        long long  k = (1ll << p) - 1;
        return k % mod * poww(k - 1, p - 1) % mod;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值