题解:洛谷 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
【核心思想】
-
问题分析:给定区间 \([l, r]\),统计其中能表示为两个 \(2\) 的次幂之和的数的个数,即 \(n = 2^x + 2^y\)(\(x, y \geq 0\))。这是一个位运算 + 枚举问题,核心在于利用二进制性质识别幂和数的特征。
-
算法选择:
- 二进制特征分析:\(2^x\) 的二进制表示只有第 \(x\) 位为 \(1\)。两个不同的 \(2\) 的次幂之和的二进制表示恰好有 \(2\) 个 \(1\);若 \(x = y\),则 \(n = 2^{x+1}\),二进制只有 \(1\) 个 \(1\)
- 枚举验证:遍历区间内的每个数,检查其二进制表示中 \(1\) 的个数是否为 \(2\)(或枚举所有可能的 \(2^x + 2^y\) 组合)
-
关键步骤:
- 读入数据:读取 \(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\)
-
时间/空间复杂度:
- 时间复杂度:\(O((r-l+1) \cdot \log r)\) 或 \(O(\log^2 r)\),取决于枚举策略
- 空间复杂度:\(O(1)\)
-
位运算与二进制特征的核心思想:
- 幂和数的二进制特征:\(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
浙公网安备 33010602011771号