GESP2506二级真题:幂和数 深度解析

四季读书网 2 0
GESP2506二级真题:幂和数 深度解析

一、题目描述

对于正整数 n,如果 n 可以表示为两个 2 的次幂之和,即 n = 2^x + 2^y(x,y 均为非负整数),那么称 n 为幂和数
给定正整数 l,r,请你求出满足 l<n<r 的整数 n中有多少个幂和数。

输入输出格式

输入:一行,两个正整数 l,r,表示区间的左右端点。
输出:一行,一个整数,表示区间l,r 内幂和数的数量。

样例说明

输入样例 1:2 8      输出样例 1:6
区间内的幂和数为:
2(2^1+2^0)、3(2^1+2^0)、4(2^2+2^0)、
5(2^2+2^0)、6(2^2+2^1)、8(2^3+2^0),共 6 个。
输入样例 2:10 100 
输出样例 2:20

数据范围

对于所有测试点,保证 1<l<<r<< 10^4。

二、题目分析

1. 核心定义拆解

幂和数的本质是:二进制表示中,1 的个数恰好为 1 或 2 的正整数
当 x = y 时:n = 2^x + 2^x = 2^(x+1),二进制只有 1 个 1(即 2 的整数次幂本身)。
当 x!=y 时:n = 2^x + 2^y,二进制恰好有 2 个 1。

2. 问题本质

我们需要统计区间 l,r 内,所有满足「二进制中 1 的个数为 1 或 2」的正整数的数量。

3. 数据范围分析

r的最大值为 10^4,数值范围极小,因此可以采用暴力枚举预生成所有幂和数等多种简单高效的方法,无需复杂算法。

三、题目思路

思路 1:预生成所有幂和数 + 区间统计(最优)

步骤 1:生成所有可能的幂和数

因为 r<= 10^4,我们可以枚举所有非负整数x,y,计算 2^x + 2^y,并将结果存入集合(自动去重)。
2 的次幂最大不超过 10^4,因此 2^{13}=8192,2^{14}=16384>10^4,所以 x,y 只需枚举到 13 即可,总枚举量仅 14* 14 = 196 次,计算量极低。

步骤 2:统计区间内的数量

遍历集合中的所有幂和数,统计满足 1<l<r 的元素个数,即为答案。

思路 2:逐数判断二进制 1 的个数

步骤 1:遍历区间内所有数

从 l到 r 逐个枚举每个数 n。

步骤 2:统计二进制中 1 的个数

对每个 n,计算其二进制中 1 的数量,若数量为 1 或 2,则计数 + 1。

步骤 3:输出总计数

遍历完成后,输出总计数。

两种思路对比

思路

优点

缺点

适用场景

预生成法

计算量极小,效率极高,代码简洁

需提前枚举所有可能的幂和数

本题数据范围,最优选择

逐数判断法

逻辑直观,无需预生成,扩展性强

需遍历最多 10^4 个数,计算量略大

数据范围更大、无法预生成的场景


四、C++ 代码实现

版本 1:预生成所有幂和数(推荐)

#include<iostream>#include<unordered_set>using namespace std;intmain(){    // 1. 预生成所有可能的幂和数,存入集合去重    unordered_set<int> s;    for (int x = 0; x <= 13; x++) { // 2^13=8192 ≤ 10^4,2^14=16384>10^4        for (int y = 0; y <= 13; y++) {            int num = (1 << x) + (1 << y); // 等价于2^x + 2^y            if (num <= 10000) { // 保证不超过数据范围上限                s.insert(num);            }        }    }    // 2. 输入区间l, r    int l, r;    cin >> l >> r;    // 3. 统计区间内的幂和数数量    int ans = 0;    for (int num : s) {        if (num >= l && num <= r) {            ans++;        }    }    // 4. 输出结果    cout << ans << endl;    return 0;}

版本 2:逐数判断二进制 1 的个数

#include<iostream>using namespace std;// 统计n的二进制中1的个数intcountOne(int n){    int cnt = 0;    while (n > 0) {        cnt += n & 1// 取最低位,若为1则计数+1        n >>= 1;      // 右移一位    }    return cnt;}intmain(){    int l, r;    cin >> l >> r;    int ans = 0;    // 遍历区间内所有数    for (int n = l; n <= r; n++) {        int cnt = countOne(n);        // 二进制1的个数为1或2,即为幂和数        if (cnt == 1 || cnt == 2) {            ans++;        }    }    cout << ans << endl;    return 0;}

五、考察知识点

1. 位运算基础

2 的次幂的表示:1 << x 等价于 2^x,是位运算的核心应用。
二进制 1 的个数统计:通过 n & 1 取最低位、n >>= 1 右移,逐位统计 1 的数量,是位运算的经典题型。

2. 集合的去重特性

使用 unordered_set 自动去重,避免重复统计相同的幂和数(如 2^1+2^0 和 2^0+2^1 结果相同)。

3. 枚举法的应用

根据数据范围选择合适的枚举策略:
本题数据范围小,直接枚举所有可能的 x,y 即可,无需优化。
若数据范围扩大,可优化枚举顺序(如 x <=y),减少重复计算。

4. 数学本质理解

将「两个 2 的次幂之和」转化为「二进制中 1 的个数为 1 或 2」,是解题的关键,考察对二进制表示的深刻理解。


六、总结

本题是 GESP 二级的经典位运算题目,核心考察二进制表示的理解位运算的基础应用
解题的关键是将「两个 2 的次幂之和」转化为「二进制 1 的个数为 1 或 2」,实现问题的简化。
两种代码实现各有优劣,预生成法更适合本题数据范围,逐数判断法更具通用性。
代码中涉及的位运算、集合去重、枚举法等知识点,是 C++ 编程的基础核心技能,也是后续算法学习的重要铺垫。

抱歉,评论功能暂时关闭!