题解:洛谷 B4357 幂和数

【题目来源】

洛谷:B4357 [GESP202506 二级] 幂和数 - 洛谷

【题目描述】

对于正整数 \(n\),如果 \(n\) 可以表为两个 \(2\) 的次幂之和,即 \(n=2^x+2^y\)\(x,y\) 均为非负整数),那么称 \(n\) 为幂和数。

给定正整数 \(l,r\),请你求出满足 $l≤n≤r $ 的整数 \(n\) 中有多少个幂和数。

【输入】

一行,两个正整数 \(l,r\),含义如上。

【输出】

输出一行,一个整数,表示 \(l,r\) 之间幂和数的数量。

【输入样例】

2 8

【输出样例】

6

【核心思想】

  1. 问题分析:给定区间 \([l, r]\),统计其中能表示为两个 \(2\) 的次幂之和的数的个数,即 \(n = 2^x + 2^y\)\(x, y \geq 0\))。这是一个位运算 + 枚举问题,核心在于利用二进制性质识别幂和数的特征。

  2. 算法选择

    • 二进制特征分析\(2^x\) 的二进制表示只有第 \(x\) 位为 \(1\)。两个不同的 \(2\) 的次幂之和的二进制表示恰好有 \(2\)\(1\);若 \(x = y\),则 \(n = 2^{x+1}\),二进制只有 \(1\)\(1\)
    • 枚举验证:遍历区间内的每个数,检查其二进制表示中 \(1\) 的个数是否为 \(2\)(或枚举所有可能的 \(2^x + 2^y\) 组合)
  3. 关键步骤

    • 读入数据:读取 \(l, r\)
    • 枚举区间内的数\(i\)\(l\)\(r\)):
      • 检查 \(i\) 的二进制表示中 \(1\) 的个数
      • 若恰好为 \(2\)\(1\)\(cnt \leftarrow cnt + 1\)(表示 \(i = 2^x + 2^y\)\(x \neq y\)
      • 或枚举 \(x\)\(0\)\(\log_2(r)\)\(y\)\(x\)\(\log_2(r)\),计算 \(2^x + 2^y\) 并判断是否在区间内
    • 输出结果\(cnt\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O((r-l+1) \cdot \log r)\)\(O(\log^2 r)\),取决于枚举策略
    • 空间复杂度:\(O(1)\)
  5. 位运算与二进制特征的核心思想

    • 幂和数的二进制特征\(n = 2^x + 2^y\) 的二进制表示中,第 \(x\) 位和第 \(y\) 位为 \(1\),其余为 \(0\)。因此幂和数的二进制中恰好有 \(2\)\(1\)(当 \(x \neq y\))或 \(1\)\(1\)(当 \(x = y\),此时 \(n = 2^{x+1}\) 也是 \(2\) 的幂)
    • 快速统计 \(1\) 的个数:利用 __builtin_popcount(i) 或逐位检查,在 \(O(\log r)\) 时间内统计二进制中 \(1\) 的个数
    • 双重枚举优化:直接枚举所有 \(2^x + 2^y\) 的组合(\(x \leq y\)),判断结果是否在 \([l, r]\) 内,避免遍历整个区间,复杂度降至 \(O(\log^2 r)\)
    • 去重处理:当 \(x = y\)\(n = 2^{x+1}\),这是 \(2\) 的幂,也满足"两个 \(2\) 的次幂之和"(相同次幂),需根据题意决定是否计入
    • 适用于位运算特征分析、基于二进制表示的数论判定类问题

【算法标签】

普及- #位运算

【代码详解】

#include <bits/stdc++.h>
using namespace std;

int l, r;    // 输入的范围区间 [l, r]
int cnt = 0; // 计数器,统计满足条件的数字个数

// 检查数字的二进制表示是否恰好有1个1(即是否为2的幂次)
bool check(int x)
{
    int res = 0;  // 记录二进制中1的个数

    // 遍历数字的每一位
    while (x)
    {
        // 检查最低位是否为1
        if (x & 1 == 1)
            res++;

        // 右移一位,检查下一位
        x >>= 1;
    }

    // 如果二进制中只有1个1,返回true
    return res == 1;
}

int main()
{
    cin >> l >> r;  // 输入范围 [l, r]

    // 遍历区间内的每一个数字
    for (int i = l; i <= r; i++)
    {
        // 尝试所有可能的2的幂次作为除数
        for (int x = 0; ; x++)
        {
            // 如果2^x超过当前数字i,终止循环
            if (pow(2, x) > i)
                break;

            // 计算i除以2^x的整数部分
            int k = i / pow(2, x);

            // 检查是否能整除(即i是否是2^x的整数倍)
            if (k * pow(2, x) != i)
                break;

            // 检查k-1是否是2的幂次
            if (check(k - 1))
                cnt++;  // 满足条件则计数器加1
        }
    }

    // 输出满足条件的数字总数
    cout << cnt << endl;

    return 0;
}

【运行结果】

2 8
6
posted @ 2026-07-17 13:48  团爸讲算法  阅读(6)  评论(0)    收藏  举报