一、单项选择题
(共 15 题,每题 2 分,共计 30 分;每题有且仅有一个正确选项)
CSPJ-2026-R1-Mock-AI-1
试卷解析总览,可直接查看每题答案与解析。
(共 15 题,每题 2 分,共计 30 分;每题有且仅有一个正确选项)
在 Linux 终端执行命令 `g++ main.cpp -o app`,若编译成功,通常会得到( )。
正确答案B
【答案】B
【考点】g++ 编译命令
【解析】 `-o app` 指定输出文件名为 `app`;编译成功后生成该可执行文件,但不会自动运行。
【易错点】 不要把输出文件名误认为源文件扩展名。
若一个 `unsigned char` 恰好占 8 个二进制位,则它能表示的整数范围是( )。
正确答案D
【答案】D
【考点】无符号整数范围
【解析】 8 位无符号整数共有 种状态,因此取值为 到 。
【易错点】 无符号类型没有符号位,最小值是 0。
执行下列程序后,输出结果是( )。
void change(int &x, int y) {
x += y;
y *= 2;
}
int a = 3, b = 4;
change(a, b);
cout << a << " " << b;正确答案A
【答案】A
【考点】引用传参与值传参
【解析】 `x` 是引用,执行 `x += y` 后 `a` 变为 7;`y` 是值传递,修改副本不会影响 `b`,故输出 `7 4`。
【易错点】 能在函数内修改形参,不代表一定会修改实参。
执行下列代码后,字符串 `s` 的值是( )。
string s = "cspj";
s.erase(1, 2);
s.insert(1, "++");正确答案C
【答案】C
【考点】string 修改操作
【解析】 `erase(1, 2)` 删除下标 1 开始的 `sp`,得到 `cj`;再在下标 1 插入 `++`,得到 `c++j`。
【易错点】 `erase` 的第二个参数是删除字符数,不是结束下标。
十进制整数 42 的二进制表示为 `101010`。表达式 `42 ^ (1 << 3)` 的值是( )。
正确答案D
【答案】D
【考点】按位异或
【解析】 `1 << 3` 等于 8,42 的第 3 位为 1;与 8 异或会把该位翻转为 0,所以结果为 。
【易错点】 异或是翻转对应位,不等同于加法。
二进制数 与八进制数 之和,用十六进制表示为( )。
正确答案C
【答案】C
【考点】进制转换
【解析】 ,,两者之和为 68,即十六进制的 。
【易错点】 八进制的 27 不是十进制的 27。
一棵完全二叉树共有 2026 个结点,则其叶结点数为( )。
正确答案B
【答案】B
【考点】完全二叉树
【解析】 顺序编号为 1 到 2026 时,编号不超过 的结点有孩子,因此叶结点数为 。
【易错点】 完全二叉树不要求所有叶结点处于同一层。
一棵二叉树的前序遍历为 `A B D E C F G`,中序遍历为 `D B E A F C G`,则其后序遍历为( )。
正确答案A
【答案】A
【考点】二叉树遍历还原
【解析】 前序首元素确定根为 A,中序将左右子树划分为 `D B E` 和 `F C G`;分别还原后,后序为 `D E B F G C A`。
【易错点】 每次递归划分时都要使用当前子树的前序首元素作为根。
元素 `1,2,3,4,5` 按此顺序依次入栈,入栈和出栈操作可以交错进行。下列不可能得到的出栈序列是( )。
正确答案C
【答案】C
【考点】栈的出栈序列
【解析】 要先输出 3,栈中必然已有 `1,2,3`;弹出 3 后栈顶是 2,不可能越过 2 先输出 1,因此 `3,1,2,5,4` 不合法。
【易错点】 判断时必须保留尚未弹出的栈内元素。
下面哪一个序列可以作为一个有 5 个顶点的简单无向连通图的度数序列?( )
正确答案B
【答案】B
【考点】无向图度数序列
【解析】 可由一个 5 阶环再增加一条弦得到,且图保持连通;其余选项度数和为奇数或含孤立点。
【易错点】 度数和为偶数只是必要条件,还需满足可实现性与连通性。
在下列排序算法的常见实现中,通常不具有稳定性的是( )。
正确答案D
【答案】D
【考点】排序稳定性
【解析】 选择排序交换最小元素与当前位置时,可能跨过相等元素并改变其相对次序,因此常见实现不稳定。
【易错点】 归并排序只有在相等时优先取左段元素才保持稳定。
长度为 8 的二进制串中,不包含两个连续的 `1` 的串共有( )个。
正确答案A
【答案】A
【考点】递推计数
【解析】 设长度为 的合法串数为 。末位为 0 时有 种,末位为 1 时前一位必须为 0,有 种;由 得 。
【易错点】 不要把每一位都当作独立的两种选择。
函数 `f` 定义如下,则 `f(5)` 的返回值是( )。
int f(int n) {
if (n <= 1) return n + 1;
return f(n - 1) + f(n - 2);
}正确答案A
【答案】A
【考点】递归函数求值
【解析】 由边界得到 ,之后依次为 3、5、8、13,所以 `f(5)` 返回 13。
【易错点】 边界值不是常见斐波那契数列的 0 和 1。
数组 `a[1..n]` 的前缀和定义为 ,。区间 等于( )。
正确答案B
【答案】B
【考点】前缀和
【解析】 `p[r]` 包含前 项,减去前 项后,恰好留下下标 到 的元素和。
【易错点】 左端点为 时应减 `p[l-1]`。
设 为正的 2 的整数次幂,且下列程序不会发生整数溢出。其时间复杂度为( )。
long long cnt = 0;
for (int i = 1; i <= n; i *= 2)
for (int j = 0; j < i; ++j)
++cnt;正确答案C
【答案】C
【考点】复杂度与等比级数
【解析】 内层执行次数依次为 ,总次数小于 ,因此时间复杂度为 。
【易错点】 外层是对数级,不代表总复杂度一定要再乘 。
假设输入字符串只包含小写英文字母,且长度为 。判断题正确填 ✓,错误填 ✗。
1 | #include <iostream>
2 | #include <string>
3 | using namespace std;
4 | string reduce_string(const string &s) {
5 | string t;
6 | for (char c : s) {
7 | if (!t.empty() && t.back() == c)
8 | t.pop_back();
9 | else
10 | t.push_back(c);
11 | }
12 | return t;
13 | }
14 | int main() {
15 | string s;
16 | cin >> s;
17 | string t = reduce_string(s);
18 | cout << t << '\n';
19 | cout << t.size() << '\n';
20 | }输出字符串长度的奇偶性一定与输入字符串长度的奇偶性相同。
正确答案正确
【答案】正确
【考点】奇偶性不变量
【解析】 每读入一个字符,`t` 的长度只会增加 1 或减少 1,因此奇偶性每次翻转;处理 个字符后长度与 同奇偶。
【易错点】 消除两个相同字符是逐字符处理产生的净效果。
若输入为 `abca`,则第一行输出为空行。
正确答案错误
【答案】错误
【考点】字符串栈模拟
【解析】 输入 `abca` 时相邻字符始终不同,四个字符都会依次压入 `t`,第一行输出 `abca`。
【易错点】 程序只消除处理时相邻的相同字符。
函数返回的字符串中一定不存在两个相邻且相同的字符。
正确答案正确
【答案】正确
【考点】栈不变量
【解析】 压入字符前要求它与当前末尾不同;弹出只会删除末尾。因此处理过程中以及最终结果中都不会出现相邻相同字符。
【易错点】 不能只检查原字符串中是否存在相邻重复。
若将第 8 行的 `t.pop_back()` 改为 `t.clear()`,程序对任意输入的输出都不变。
正确答案错误
【答案】错误
【考点】程序修改与反例
【解析】 输入 `abbc` 时原程序得到 `ac`;若改为 `clear()`,处理两个 `b` 后会清空整个 `t`,最终得到 `c`,输出发生变化。
【易错点】 `pop_back()` 只删除一个字符,`clear()` 会删除全部字符。
若输入为 `abbaca`,则第一行输出为( )。
正确答案B
【答案】B
【考点】程序输出模拟
【解析】 依次处理 `abbaca`:两个 `b` 抵消,随后两个 `a` 抵消,剩余字符依次为 `c`、`a`,第一行输出 `ca`。
【易错点】 每次消除后要继续使用新的栈顶判断后续字符。
该程序的时间复杂度和额外空间复杂度分别为( )。
正确答案D
【答案】D
【考点】摊还复杂度
【解析】 每个输入字符只进行一次判断和一次压入或弹出,时间为 ;最坏情况下 `t` 保存全部字符,额外空间为 。
【易错点】 字符串尾部压入、弹出的摊还代价为常数。
输入保证 、,并且数组中的所有元素均为正整数。判断题正确填 ✓,错误填 ✗。
1 | #include <iostream>
2 | #include <vector>
3 | using namespace std;
4 | int main() {
5 | int n;
6 | long long k;
7 | cin >> n >> k;
8 | vector<long long> a(n);
9 | for (int i = 0; i < n; ++i) cin >> a[i];
10 | long long ans = 0, sum = 0;
11 | int left = 0;
12 | for (int right = 0; right < n; ++right) {
13 | sum += a[right];
14 | while (left <= right && sum > k) {
15 | sum -= a[left];
16 | ++left;
17 | }
18 | ans += right - left + 1;
19 | }
20 | cout << ans << '\n';
21 | return 0;
22 | }每次执行完第 14~17 行的 `while` 循环后,都有 `sum <= k`。
正确答案正确
【答案】正确
【考点】滑动窗口不变量
【解析】 数组元素均为正数,窗口和超出 时不断移动左端点;循环停止时窗口为空或其元素和已经不超过 。
【易错点】 结论依赖所有数组元素为正。
将第 14 行的 `while` 改为 `if`,程序对所有合法输入的输出都不变。
正确答案错误
【答案】错误
【考点】循环条件与反例
【解析】 例如 、数组为 `1 1 3`。加入 3 后窗口和为 5,只删除一个 1 仍为 4,必须继续删除,因此不能把 `while` 改成 `if`。
【易错点】 一次缩短窗口未必足以恢复合法状态。
程序输出的是数组中元素和不超过 的连续非空子数组个数。
正确答案正确
【答案】正确
【考点】双指针计数
【解析】 固定右端点后,`left` 是最靠左的合法起点;以 `right` 结尾的合法非空子数组共有 `right-left+1` 个,累加后得到全部答案。
【易错点】 题目统计的是连续子数组,不是任意子序列。
由于程序中存在两层循环,因此最坏时间复杂度是 。
正确答案错误
【答案】错误
【考点】双指针复杂度
【解析】 虽然 `while` 位于 `for` 内,但 `left` 在整个程序中最多从 0 增加到 ,两个指针的总移动次数均为 。
【易错点】 不要把嵌套写法直接等同于平方复杂度。
若输入如下,则程序输出为( )。
5 5
1 2 1 3 2正确答案C
【答案】C
【考点】滑动窗口模拟
【解析】 五个右端点分别新增 1、2、3、2、2 个合法子数组,累计为 。
【易错点】 每次只需统计以当前右端点结尾的合法区间。
对于固定的 ,程序输出的最大可能值是( )。
正确答案A
【答案】A
【考点】子数组总数
【解析】 当 足够大时所有连续非空子数组都合法。长度为 的数组共有 n+(n-1)+cdots+1=rac{n(n+1)}{2} 个连续非空子数组。
【易错点】 连续子数组的数量不是 。
输入保证 ,且 均为非负整数。判断题正确填 ✓,错误填 ✗。
1 | #include <algorithm>
2 | #include <iostream>
3 | #include <vector>
4 | using namespace std;
5 | int main() {
6 | int n;
7 | cin >> n;
8 | vector<long long> a(n + 1), dp(n + 1, 0);
9 | for (int i = 1; i <= n; ++i) cin >> a[i];
10 | dp[1] = a[1];
11 | for (int i = 2; i <= n; ++i)
12 | dp[i] = max(dp[i - 1], dp[i - 2] + a[i]);
13 | cout << dp[n] << '\n';
14 | return 0;
15 | }`dp[i]` 表示从前 个数中选择若干个互不相邻的数时,能够得到的最大元素和。
正确答案正确
【答案】正确
【考点】一维动态规划
【解析】 最优方案要么不选第 个数,价值为 `dp[i-1]`;要么选它并跳过第 个数,价值为 `dp[i-2]+a[i]`,两者取最大。
【易错点】 状态描述的是前缀范围内的最优值,不要求必须选择第 个数。
对于任意 ,都有 `dp[i] <= dp[i + 1]`。
正确答案正确
【答案】正确
【考点】动态规划单调性
【解析】 递推式显式取 `max(dp[i-1], ...)`,所以 `dp[i]` 不会小于 `dp[i-1]`。
【易错点】 单个数组元素大小可以下降,但前缀最优值不会下降。
将第 12 行改为 `dp[i] = dp[i - 2] + a[i]`,程序对所有合法输入的输出都不变。
正确答案错误
【答案】错误
【考点】状态转移完整性
【解析】 若只保留 `dp[i-2]+a[i]`,就强制选择第 个数。输入 `2` 和 `5 1` 时会把正确答案 5 算成 1。
【易错点】 最优转移必须同时考虑选与不选。
该程序的时间复杂度为 。
正确答案错误
【答案】错误
【考点】线性时间复杂度
【解析】 程序只有一次读入循环和一次从 2 到 的递推循环,每轮为常数操作,因此时间复杂度是 。
【易错点】 没有隐藏的递归或内层枚举。
若输入如下,则程序输出为( )。
6
4 2 7 3 6 1正确答案D
【答案】D
【考点】动态规划模拟
【解析】 `dp[1..6]` 依次为 `4,4,11,11,17,17`,对应可选择 4、7、6,最大和为 17。
【易错点】 不能同时选择相邻位置上的数。
若所有 都等于 1,则程序输出为( )。
正确答案B
【答案】B
【考点】不相邻选择计数
【解析】 所有权值都是 1 时,只需选择尽可能多的不相邻位置,最多可取第 1、3、5……个位置,共 个。
【易错点】 当 为奇数时答案比 多 1。
给定一个 的迷宫,字符 `.` 表示可以通过,字符 `#` 表示障碍。起点和终点坐标均从 0 开始编号,且保证起点、终点不是障碍。每次可以向上、下、左、右移动一格。程序输出从起点到终点的最少移动次数;若无法到达则输出 `-1`。
1 | #include <iostream>
2 | #include <queue>
3 | #include <string>
4 | #include <utility>
5 | #include <vector>
6 | using namespace std;
7 | int main() {
8 | int n, m, sx, sy, tx, ty;
9 | cin >> n >> m;
10 | vector<string> g(n);
11 | for (int i = 0; i < n; ++i) cin >> g[i];
12 | cin >> sx >> sy >> tx >> ty;
13 | vector<vector<int>> dist(n, vector<int>(m, -1));
14 | queue<pair<int, int>> q;
15 | ① = 0;
16 | q.push(②);
17 | int dx[4] = {-1, 1, 0, 0};
18 | int dy[4] = {0, 0, -1, 1};
19 | while (!q.empty()) {
20 | pair<int, int> cur = q.front();
21 | int x = cur.first, y = cur.second;
22 | ③;
23 | for (int d = 0; d < 4; ++d) {
24 | int nx = x + dx[d], ny = y + dy[d];
25 | if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
26 | if (④) {
27 | dist[nx][ny] = ⑤;
28 | q.push(make_pair(nx, ny));
29 | }
30 | }
31 | }
32 | cout << dist[tx][ty] << '\n';
33 | return 0;
34 | }① 处应填( )。
dist[tx][ty]dist[0][0]dist[sx][sy]dist[sy][sx]正确答案C
【答案】C
【考点】BFS 起点初始化
【解析】 最短路从起点开始,起点到自身的距离为 0,因此应设置 `dist[sx][sy] = 0`。
【易错点】 目标点距离应由搜索过程得到,不能预先设为 0。
② 处应填( )。
make_pair(sx, sy)make_pair(tx, ty)make_pair(0, 0)make_pair(sy, sx)正确答案A
【答案】A
【考点】BFS 队列初始化
【解析】 队列中首先应放入起点坐标 `make_pair(sx, sy)`,搜索才能从起点向四周扩展。
【易错点】 终点只用于读取最终距离。
③ 处应填( )。
q.push(cur)q.front()q.back()q.pop()正确答案D
【答案】D
【考点】队列操作
【解析】 读取 `q.front()` 后必须执行 `q.pop()` 删除当前结点,否则它会被反复处理,循环无法正常推进。
【易错点】 `front()` 只读取队首,不会自动出队。
④ 处应填( )。
g[nx][ny] == '#' && dist[nx][ny] == -1g[nx][ny] != '#' && dist[nx][ny] == -1g[nx][ny] != '#' && dist[nx][ny] != -1g[nx][ny] == '#' || dist[nx][ny] == -1正确答案B
【答案】B
【考点】BFS 可达条件
【解析】 相邻格既要不是障碍 `#`,又要尚未访问,即 `dist[nx][ny] == -1`,满足两者时才能入队。
【易错点】 只判断可通行而不判断访问状态会重复入队。
⑤ 处应填( )。
dist[nx][ny] + 1dist[sx][sy] + 1dist[x][y] + 1dist[x][y] - 1正确答案C
【答案】C
【考点】BFS 距离转移
【解析】 从当前格移动一步到相邻格,因此新格距离应为 `dist[x][y] + 1`。
【易错点】 新格原来的距离为 -1,不能在它的旧值上加 1。
给定 和 ,其中 。程序先预处理不超过 的所有素数,然后回答 次询问。每次给出 ,输出区间 内素数的个数。
1 | #include <iostream>
2 | #include <vector>
3 | using namespace std;
4 | int main() {
5 | int n, q;
6 | cin >> n >> q;
7 | vector<bool> is_prime(n + 1, true);
8 | is_prime[0] = is_prime[1] = false;
9 | for (int i = 2; ①; ++i)
10 | if (is_prime[i])
11 | for (int j = ②; j <= n; j += i)
12 | is_prime[j] = ③;
13 | vector<int> pre(n + 1, 0);
14 | for (int i = 1; i <= n; ++i)
15 | pre[i] = ④;
16 | while (q--) {
17 | int l, r;
18 | cin >> l >> r;
19 | cout << ⑤ << '\n';
20 | }
21 | return 0;
22 | }① 处应填( )。
i * i <= ni <= ni < n / 2i * 2 <= n正确答案A
【答案】A
【考点】埃氏筛枚举上界
【解析】 每个合数至少有一个不超过其平方根的质因数,所以只需枚举到 `i * i <= n`。
【易错点】 循环到 虽可能得到结果,但会破坏标准筛法的复杂度设计。
② 处应填( )。
2 * ii + 1i * in / i正确答案C
【答案】C
【考点】埃氏筛起点
【解析】 当处理质数 时,小于 的倍数已经被更小的质因数筛过,因此从 `i * i` 开始即可。
【易错点】 从 `2*i` 开始会重复标记大量合数。
③ 处应填( )。
truefalseji正确答案B
【答案】B
【考点】素数筛标记
【解析】 `j` 是质数 的倍数且不小于 ,一定是合数,所以应令 `is_prime[j] = false`。
【易错点】 数组含义是“是否为素数”,标记方向不要写反。
④ 处应填( )。
pre[i] + is_prime[i]pre[i - 1] + ipre[i - 1] + is_prime[i - 1]pre[i - 1] + (is_prime[i] ? 1 : 0)正确答案D
【答案】D
【考点】素数前缀和
【解析】 `pre[i]` 应等于前 个数中的素数个数,再加上 自身是否为素数,即 `pre[i-1] + (is_prime[i] ? 1 : 0)`。
【易错点】 前缀状态必须从 `pre[i-1]` 转移。
⑤ 处应填( )。
pre[r] - pre[l - 1]pre[r] - pre[l]pre[r - 1] - pre[l - 1]pre[r] + pre[l - 1]正确答案A
【答案】A
【考点】区间计数
【解析】 `pre[r]` 统计 1 到 的素数个数,减去 1 到 的个数后,得到闭区间 的答案。
【易错点】 左端点 本身属于查询区间,因此减 `pre[l-1]`。