题解 | #牛妹的位运算#

牛妹的位运算

https://www.nowcoder.com/practice/7f131dd26f4043839d0342ed6f65ddf3

题目链接

牛妹的位运算

题目描述

给定一个正整数 ,要求在区间 内寻找满足以下所有条件的非负整数对 的数量:

结果需要对 取模。

解题思路

这是一个位运算相关的计数问题。核心在于理解并简化条件

条件转换

让我们从二进制表示的角度来分析这个不等式。 设 。为了比较 的大小,我们通常从它们的最高位开始比较。

中最高有效位(Most Significant Bit, MSB)的位置。也就是说, 是使得 的第 位或 的第 位为 1 的最大下标。对于所有 的第 位都为 0。

  • 对于所有 的第 位显然也都是 0。
  • 现在考虑第 位:
    • 因为 是最高有效位, 在第 位上不能都为 0。
    • 情况 1 在第 位上分别为 1 和 0(或 0 和 1)。
      • 的第 位是
      • 的第 位是
      • 在这种情况下, 在它与 的第一个不同位上是 1,所以
    • 情况 2 在第 位上都为 1。
      • 的第 位是
      • 的第 位是
      • 在这种情况下, 在它与 的第一个不同位上是 1,所以

由此我们得出结论:不等式 成立,当且仅当在 的最高公共有效位 上,它们的值不相同。这等价于说, 的最高有效位在不同的位置上。 为方便起见,我们定义 的最高有效位的索引(例如 ),并定义 。 那么,原条件就等价于

组合计数

现在问题转化为:计算满足 的数对 的数量。

我们可以分情况讨论:

  1. 。条件变为 ,这意味着 不能为 0。 又因为 ,所以 恒成立。 因此, 可以是 内的任何整数。 这种情况下的数对数量为

  2. : 设 。 条件 意味着 。 条件 意味着 。 结合两者,我们必须有

    我们对 进行枚举:

    • 可以从 取到
    • 对于每个固定的 可以从 取到

    对于固定的 (其中 ):

    • 满足 的数量为 (即从 )。
    • 满足 的数量为 (即从 )。
    • 因为 ,任何满足 都小于任何满足 ,所以 恒成立。
    • 因此,对于固定的 ,有

    总数量为 。 内部的和是等比数列求和:。 所以,和式变为 。 再次使用等比数列求和公式:

    • 这部分的数量为

合并结果

总数 = (情况1的数量) + (情况2的数量)


我们只需要计算 即可。这需要用到快速幂和模逆元。

代码

#include <iostream>

using namespace std;

long long power(long long base, long long exp) {
    long long res = 1;
    base %= 1000000007;
    while (exp > 0) {
        if (exp % 2 == 1) res = (res * base) % 1000000007;
        base = (base * base) % 1000000007;
        exp /= 2;
    }
    return res;
}

long long modInverse(long long n) {
    return power(n, 1000000007 - 2);
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    int k;
    cin >> k;
    
    long long p4k = power(4, k);
    long long numerator = (p4k - 1 + 1000000007) % 1000000007;
    long long inv3 = modInverse(3);
    
    long long ans = (numerator * inv3) % 1000000007;
    
    cout << ans << endl;
    
    return 0;
}
import java.util.Scanner;

public class Main {
    static long power(long base, long exp) {
        long res = 1;
        long mod = 1000000007;
        base %= mod;
        while (exp > 0) {
            if (exp % 2 == 1) res = (res * base) % mod;
            base = (base * base) % mod;
            exp /= 2;
        }
        return res;
    }

    static long modInverse(long n) {
        long mod = 1000000007;
        return power(n, mod - 2);
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int k = sc.nextInt();
        long mod = 1000000007;

        long p4k = power(4, k);
        long numerator = (p4k - 1 + mod) % mod;
        long inv3 = modInverse(3);

        long ans = (numerator * inv3) % mod;

        System.out.println(ans);
    }
}
def power(base, exp):
    res = 1
    mod = 10**9 + 7
    base %= mod
    while exp > 0:
        if exp % 2 == 1:
            res = (res * base) % mod
        base = (base * base) % mod
        exp //= 2
    return res

def mod_inverse(n):
    mod = 10**9 + 7
    return power(n, mod - 2)

def solve():
    k = int(input())
    mod = 10**9 + 7
    
    p4k = power(4, k)
    numerator = (p4k - 1 + mod) % mod
    inv3 = mod_inverse(3)
    
    ans = (numerator * inv3) % mod
    print(ans)

solve()

算法及复杂度

  • 算法:数学 + 组合计数 + 模运算。通过分析位运算性质将问题转化为计数问题,并通过推导数学公式得到封闭解。
  • 时间复杂度:计算结果主要依赖于快速幂运算,其时间复杂度为
  • 空间复杂度:算法只需要常数个变量,空间复杂度为
全部评论

相关推荐

牛客小菜鸡66:boss里面,招人的叫老板,找工作的叫牛人
点赞 评论 收藏
分享
头像
11-03 16:48
已编辑
百度_高级研发工程师
事实是检验真理的唯一标准。&nbsp;无论我们怎么去说,去讲述,去证明,都抵不过一个offer来得实在,无论我们怎么去复现求职中的摸爬滚打、扒皮抽筋、狼狈不堪,都抵不过你在简历写上大厂的名字(外包不算)。&nbsp;所以在我求职期间,我什么话都不说,什么话都不讲,因为没有意义,虽然我总讲过程才是意义,但只有当你上岸的那一刻,你才有资格回想在水里的挣扎,只有等你出了山,你才知道山的全貌。&nbsp;我为什么一定要离开华为OD,难道它不稳定吗,不能赚钱吗。为了证明自己,那肯定有的。其实更多的是印证我的认知是否真的正确。&nbsp;(给不了解我的人交代一下背景,在下双非一本,gap一年,华为OD外包,摸爬滚打4个月,艰难上岸百度正编)一、...
先锋战士:说得很真诚。鄙视链自古有之,学历,家庭背景,财富,权利。从小有之,小学羡慕那些当班委的,中学羡慕那些学生会的,高中羡慕尖子班拿教学金的,大学羡慕高绩点,毕业了羡慕进大厂的。工作了,又羡慕高职级的,再后来又羡慕别人早早结婚的。我想表达的观点很简单,无论是华为od还是百度,都是经历,没有孰高孰低,为了抵达下一个风景,总会付出更多东西,但不就是人生吗?正如登山,每个阶段的山,都要想办法攀登,在博主的文字中,见到了坚持和积极寻找问题解决办法的心态
学历对求职的影响
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务