第十六届蓝桥杯省赛Python研究生组-F串

  • 考点:二分查找 + 数位统计
  • 题目描述:
  • 解题思路1:挨个统计
python 复制代码
import sys


def main():
    x = int(sys.stdin.readline().strip())
    total_1 = 0  # 统计1的总个数
    total_len = 0  # 已经拼接的总长度
    num = 0  # 从0开始拼接!

    while total_len < x:
        # 1. 把当前数字转成二进制字符串(去掉0b)
        binary = bin(num)[2:]
        # 2. 还能截取多少位:最多取到x位,不超额
        need = x - total_len
        # 3. 截取当前二进制的前 need 位
        cut = binary[:need]
        # 4. 统计这一段里的1,加到总数
        total_1 += cut.count('1')
        # 5. 更新总长度
        total_len += len(cut)
        # 6. 下一个数字
        num += 1

    return total_1


# 主程序
if __name__ == "__main__":
    main()
  • 解题思路2:

目标:前 n 位里有多少个 1

问题拆分:定位 + 统计

  1. 定位,前 n 位是**"哪些完整数字的二进制" + "最后一个不完整数字的前 offset 位"**

如何定位?------二分查找:找到最大的数字 target,使得前 target 个数字的二进制总长度 < n

  1. 统计,"完整数字中 1 的总数" + "最后一个数字前 offset 位中 1 的个数"

如何统计?------数位贡献:各数字二进制的第 i 位,每 个数为一个周期,每个周期中有 个 1,且前半周期是 0,后半周期是 1

  • 核心函数思路:返回 [0,n] 拼接成二进制串后的长度和其中 1 的个数
  1. 总长度

按位分段:除了0以外,二进制有1位的有 个数字、2位的有 个、3位的有 个、4位的有 个、...k位的有 个,这个值也表示二进制有k位的最小数字 ,则二进制有k位的数字总长度为 × k

知道这个规律后,可以先获取数字 n 的二进制位数 bits_count,然后累加计算 1 到 bits_count-1 位的总长度

python 复制代码
    bits_count = n.bit_length()

    for i in range(1, bits_count):
        # i位二进制数的个数
        cur_num = 2 ** (i-1)
        length += cur_num * i

接着计算位数为 bits_count,到数字 n 截止的长度

python 复制代码
# 截止到 n 的 bits_count 位的数字个数
now_num = n - (2 ** (bits_count - 1)) + 1
length += now_num * bits_count
  1. 含1个数

数位贡献:根据前文分析,累加计算各周期中1的个数

python 复制代码
bits_count = n.bit_length()
    for i in range(bits_count):
        # 周期长度 = 2^(i+1)
        period = 2 ** (i+1)
        # 每个周期内第i位为1的次数 = 2^i
        period_count = 2 ** i
        # 完整周期数
        full = (n+1) // period
        count += full * period_count
        # 不完整周期的有效个数
        not_full = (n+1) % period
        if not_full > period_count:
            count += (not_full - period_count)
  • 参考代码:
python 复制代码
import sys



# 计算数字[0,n)拼接成二进制串后的长度,以及其中1的个数
def get_info(n):
    if n <= 0:
        return 0,0
    if n == 1:
        return 1,0

    m = n - 1
    length = 1
    count = 0

    # 统计1的个数,数位贡献
    # m的二进制位数
    bits_count = m.bit_length()
    for i in range(bits_count):
        # 周期长度 = 2^(i+1)
        period = 1 << (i+1)
        # 每个周期内第i位为1的次数 = 2^i
        period_count = 1 << i
        # 完整周期数
        full = (m+1) // period
        count += full * period_count
        # 不完整周期的有效个数
        not_full = (m+1) % period
        if not_full > period_count:
            count += (not_full - period_count)

    # 统计总长度,按位数分段,遍历i~bits_count-1位
    for i in range(1, bits_count):
        # i位二进制数的个数
        cur = 1 << (i-1)
        length += cur * i
    # 计算bits_count位的数字个数
    cur = m - (1 << (bits_count - 1)) + 1
    length += cur * bits_count

    return length, count

def main():
    x = int(sys.stdin.readline().strip())
    if x == 1:
        print(0)
        return

    # 二分查找定位目标数字:找到第 x 位在哪个数字 target 的二进制中
    # 逻辑:找最大的 target,使得前 target 个数字的长度<x
    left = 1
    # 将x作为上界
    right = x
    target = 0
    while left <= right:
        mid = (left + right) // 2
        # 取[0,mid)的总长度,忽略1的个数,_表示占位符
        length, _ = get_info(mid)
        if length < x:
            # 总长度不足,mid符合条件,目标在更大的数字里
            target = mid
            left = mid+1
        else:
            # 长度超过或与x相等,缩小上界
            right = mid-1
    # 二分查找完毕,此时target是最后一个总长度小于x的数字

    len1, sum1 = get_info(target)
    offset = x - len1
    bin_target = bin(target)[2:]

    ans = sum1 + bin_target[:offset].count('1')
    print(ans)

if __name__ == '__main__':
    main()
  • 位运算:1 << k,等同于

原理:二进制中左移一位,本质上就是乘以2

最好用位运算而不是幂运算 ** :位运算是 CPU 底层直接支持的硬件指令,速度快,而幂运算本质上是一个函数调用

相关推荐
kaico20182 小时前
面向对象和高级特性
开发语言·python
曲幽2 小时前
告别手写 API 胶水代码:FastAPI 与 Vue 的“契约自动机” OpenAPI 实战
python·typescript·vue·fastapi·web·swagger·openapi·codegen
鲸渔2 小时前
【C++ 入门】第一个程序:Hello World 与基本语法规则
开发语言·c++·算法
‎ദ്ദിᵔ.˛.ᵔ₎2 小时前
滑动窗口算法
算法·哈希算法
阿捞22 小时前
python-langchain框架(3-20-智能问答ZeroShot_ReAct Agent 从零搭建)
python·react.js·langchain
数据知道2 小时前
claw-code 源码分析:从 REPL 到服务端——CLI / HTTP(SSE) / LSP 多入口如何共享同一颗 runtime 心?
python·网络协议·http·ai·里氏替换原则·claude code
sali-tec2 小时前
C# 基于OpenCv的视觉工作流-章46-矩形卡尺
图像处理·人工智能·opencv·算法·计算机视觉
仟濹2 小时前
【算法打卡day39(2026-04-06~08 周一~周三)】(10道蓝桥杯真题)今日练习:蓝桥杯第13届省赛B组Cpp组
算法·职场和发展·蓝桥杯
不解不惑2 小时前
gemma4 实现ASR语音识别
人工智能·python·语音识别