【1】P3156 【深基15.例1】询问学号
P3156 【深基15.例1】询问学号
题目描述
有 \(n(n \le 2 \times 10^6)\) 名同学陆陆续续进入教室。我们知道每名同学的学号(在 \(1\) 到 \(10^9\) 之间),按进教室的顺序给出。上课了,老师想知道第 \(i\) 个进入教室的同学的学号是什么(最先进入教室的同学 \(i=1\)),询问次数不超过 \(10^5\) 次。
输入格式
第一行 \(2\) 个整数 \(n\) 和 \(m\),表示学生个数和询问次数。
第二行 \(n\) 个整数,表示按顺序进入教室的学号。
第三行 \(m\) 个整数,表示询问第几个进入教室的同学。
输出格式
输出 \(m\) 个整数表示答案,用换行隔开。
输入输出样例 #1
输入 #1
10 3
1 9 2 60 8 17 11 4 5 14
1 5 9
输出 #1
1
8
5
题解
(1)代码可优化点
-
数据类型 (
intvslong long):这是潜在的隐患。题目中学号上限为 \(10^9\),int(上限约 \(2.1 \times 10^9\))可以存储学号本身。但vector<int> list的索引是通过size_t(一种无符号整数类型)计算的,在处理超大数组时,为了避免索引计算可能出现的潜在溢出风险,推荐使用更能保证安全的long long或size_t类型来处理索引和大小。 -
输入输出(I/O)优化:这是最重要的优化。面对\(2\times10^6\)的数据输入量,
cin/cout的默认同步机制会拖慢速度。在你的代码开头加上这两行,代码效率会显著提升:ios::sync_with_stdio(false); cin.tie(0); -
缓存命中率与容器选择(静态数组 vs.
vector):这是一个很关键的思维拓展点。你使用的vector非常安全方便,但它的数据存储在堆内存中。而静态数组(如int a[2000005];,通常作为全局变量)存储在数据段(Data Segment)。在顺序访问的场景下,静态数组的内存访问模式更简单、可预测,对CPU缓存(Cache)更友好。而
vector的堆内存可能因为碎片化或访问堆上元数据,导致缓存未命中(Cache Miss)的次数理论上更高。在数据规模极大且访问密集时,静态数组在顺序读取速度上有微小的理论优势。
详细对比一下:
| 维度 | vector<int> |
静态数组 int arr[MAXN] |
|---|---|---|
| 内存位置 | 堆 (Heap) | 全局/静态存储区 (Data Segment) |
| 空间分配 | 动态分配,需 push_back |
编译时确定,固定大小 |
| 缓存命中率 | 对CPU缓存可能不够友好 | 对CPU缓存非常友好 |
| 安全性 | 提供 at() 越界检查,operator[] 无检查 |
无检查,越界会导致未定义行为 |
| 推荐场景 | 大小不确定、需要动态增长的场景 | 大小固定或可预估上限的场景 |
对于本题,数据规模确定(\(n \le 2\times10^6\)),使用vector完全正确。这里主要是从原理上探讨一下性能差异。
-
内存优化:你可以合并操作,不需要把第二行、第三行的所有数据都存下来。可以边输入询问边输出结果,但这样做会失去I/O优化的部分优势。
-
扩展性考量:如果题目未来询问次数极大且数据不改,使用更底层的数据结构(如普通数组)和内存管理可能会成为拉开差距的关键。
(2)代码实现
解法1:使用vector (你的思路,现代C++风格)
#include <iostream>
#include <vector>
using namespace std;
int main() {
// 加速IO
ios::sync_with_stdio(false);
cin.tie(0);
int n, m;
cin >> n >> m;
// 使用 vector 存储学号
vector<long long> a(n + 1); // 多开1个空间,使学号从 a[1] 开始存储
for (int i = 1; i <= n; ++i) {
cin >> a[i];
}
// 直接输出询问的学号
while (m--) {
int idx;
cin >> idx;
cout << a[idx] << '\n'; // 使用 '\n' 代替 endl 更快
}
return 0;
}
这是最推荐、最均衡的AC解法,兼具安全与效率。
解法2:使用静态数组 (效率最高)
#include <iostream>
using namespace std;
const int MAXN = 2000005;
long long a[MAXN]; // 全局数组,索引从1开始使用[reference:5]
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
}
while (m--) {
int idx;
cin >> idx;
cout << a[idx] << '\n';
}
return 0;
}
追求极致速度和稳定性的经典C风格解法,代码极其简洁。
关键总结
| 重点项 | 说明 |
|---|---|
| 数据范围 | \(n \le 2\times 10^6\),\(m \le 10^5\),必须重视 I/O 效率 |
| 核心优化 | 使用 ios 解绑和 \n 是解题关键 |
| 数据结构 | 解法1 (vector) 安全方便;解法2 (静态数组) 极致高效 |
| 易错点 | 数组大小:记得开 n+1 或 n+5 的余量,防止越界 |

浙公网安备 33010602011771号