一、单项选择题
(共 15 题,每题 2 分,共计 30 分;每题有且仅有一个正确选项)
试卷解析 · 理解每一步
CSPJ-2023-R1-Mock-Luogu
试卷解析总览,可直接查看每题答案与解析。
(共 15 题,每题 2 分,共计 30 分;每题有且仅有一个正确选项)
网址 `www.luogu.com.cn` 当中,顶级域名是( )。
正确答案B
域名从右向左层级升高,`www.luogu.com.cn` 最右侧的 `cn` 是国家和地区顶级域名,因此选择 B。
以下物品可以携带进 CSP 第二轮测试考场的是( )。
正确答案C
普通文化衫不属于电子设备或书籍资料,且照片已经获得授权,因此 C 可以携带;会干扰考场、含资料内容或带拍照功能的物品均不合规。
将元素 `a,b,c,d,e,f` 依次入栈,则以下选项中出栈序列不可能是( )。
正确答案D
D 中先弹出 `d,c` 后,若再压入并弹出 `f`,`e` 仍位于 `b` 上方,不可能接着弹出 `b`,所以该序列无法产生。
“流程结构”是编程中用于控制程序执行流程的一种方式,它包括顺序结构、分支结构和循环结构。在一些诗歌作品中,也有对“流程结构”的体现。下列诗歌片段中体现循环结构的是( )。
正确答案B
B 用多个“只要”描述持续条件,并以“就一直走向前方”描述条件成立时重复执行的动作,体现循环结构。
已知两个二进制整数 :,,则表达式 `(a & b) ^ (a | b)` 的值为( )。
正确答案B
对每一位都有 `(a & b) ^ (a | b) = a ^ b`;逐位异或得到 `0100101100`,对应 B。
一个有 10 个结点的有向图,要使得每一个满足 、 的点对 都存在一条从 到达 的路径,至少需要连( )条有向边。
正确答案B
每个结点至少要有一条出边,10 个结点至少需要 10 条边;用 10 条边构成一个有向环即可让任意结点互相可达。
观察下列代码,其中 `k` 的类型以及 `k` 的值分别为( )。
int a[] = {5, 4, 3, 2, 1};
auto p = a + 3;
auto q = &p;
(*q) ++;
auto k = *p;正确答案A
初始 `p=a+3`,`q` 指向变量 `p`;`(*q)++` 把 `p` 移到 `a+4`,所以 `k=*p=a[4]=1`,且 `k` 推导为 `int`。
中缀表达式 `a+(b+c)-d*e/f` 改写成后缀表达式为( )。
a b + c + d e * f / -a b c + + d e * f / -a b + c + d * e f / -a b c + + d * e / f -正确答案B
先把 `(b+c)` 写成 `b c +`,再处理 `a+...` 和 `d*e/f`,最终为 `a b c + + d e * f / -`。
一张大小为 的 24 位彩色图片,使用 `.bmp` 格式存储,占用的空间大小约为( )。
正确答案A
24 位即每像素 3 字节,空间约为 MiB,取约数为 144 MiB。
以下程序片段的时间复杂度为( )。
int cnt = 0;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j += i) {
for(int k = 1; k <= n; k += j) {
++cnt;
}
}
}提示:
$$
$$
其中 均为常数。
正确答案A
对每个 `i`,`j=1` 时内层 `k` 都执行 次,仅这一项总计已是 ;其余项总量不超过同阶,因此复杂度为 Θ(n²)。
依次抛出四个六面骰子,按照抛出顺序将骰子上的数值记为 。则 、、 同时成立的概率为( )。
正确答案A
四个骰子共有 个等可能结果;按中间值 `b,c` 枚举,满足 且 的结果共有 190 个,概率为 。
十进制小数 0.3,转写成八进制为( )。
正确答案B
反复乘 8 得到整数位序列 `2,3,1,4,6,3,1,...`,因此八进制表示为 `0.2314631...`。
观察如下代码片段,其中 `sizeof(u)` 的值为( )。
union U {
bool flag1, flag2, flag3, flag4, flag5;
signed short a;
unsigned short b;
enum E {
CardA = 0, CardB = 1,
CardC = 2, CardD = 142857
} e;
} u;正确答案A
联合体所有成员共享内存,大小由最大成员及对齐决定;枚举成员通常占 4 字节且对齐为 4,所以 `sizeof(u)=4`。
已知某种可用来维护序列的数据结构,支持 向某个位置后面插入元素、 查询某个元素的排名、 遍历整个序列,那么用上述三种操作实现插入排序的时间复杂度最坏为( )。
正确答案A
插入每个元素前需用 Θ(n) 查询排名,执行 次得到 Θ(n²);插入和最终遍历的代价更低,不改变最坏阶。
今年是 CCF(中国计算机学会)第( )次举办 CSP-J/S(计算机非专业级别的软件能力认证)?
正确答案C
CSP-J/S 从 2019 年开始举办,2023 年依次是第 5 次,因此选择 C。
1 | #include <iostream>
2 | #include <cstring>
3 | using namespace std;
4 | string s, t;
5 | string a, b;
6 | int main(){
7 | getline(cin, s);
8 | getline(cin, a);
9 | getline(cin, b);
10 | for(int i = 0; i < s.size(); i++){
11 | bool flag = true;
12 | if(i + a.size() <= s.size()){
13 | for(int j = 0; j < a.size(); j++){
14 | char p = s[i + j], q = a[j];
15 | if('a' <= p && p <= 'z') p = p - 'a' + 'A';
16 | if('a' <= q && q <= 'z') q = q - 'a' + 'A';
17 | if(p != q)
18 | flag = false;
19 | }
20 | } else
21 | flag = false;
22 |
23 | if(flag == true){
24 | t += b;
25 | i += a.size() - 1;
26 | } else
27 | t += s[i];
28 | }
29 | cout << t << endl;
30 | return 0;
31 | }保证输入的三行字符串的长度均不超过 ,且每个字符串均由 ASCII 可视字符或空格组成且非空。完成下面的判断题和单选题。
保持 `s,b` 的字母大小写不变,将 `a` 里的小写英文字母改写成大写、将大写英文字母改写成小写,输出结果不变。( )
正确答案正确
比较时 `s` 和 `a` 中的小写字母都会临时转成大写,交换 `a` 的原始大小写不会改变任何匹配位置,输出不变。
每次调用 `s.size()` 的复杂度是 ,但若把 `s.size()` 替换成 `s.length()`,则调用的复杂度将会变成 ,其中 是 `s` 当前的长度。这是因为 `s.length()` 作为 `strlen()` 函数的 `string` 版本,会每次重新计算 `s` 最后一个元素的位置。( )
正确答案错误
`std::string::size()` 与 `length()` 语义等价,通常都直接返回已保存的长度,复杂度均为 ;`length()` 不是 `strlen()` 的逐次扫描包装。
当输入的字符串 `s,a,b` 均由小写字母 `a` 或 `b` 组成,记 `s,a,b` 的长度分别为 ,则程序的时间复杂度最坏为 。( )
正确答案正确
最坏情况下每个起点比较至多 个字符,代价 ;向结果串追加替换串的总上界为 ,合计 。
当输入的字符串 `s,a,b` 均由小写字母 `a` 组成,记 `s,a,b` 的长度分别为 且有 ,那么上述程序的总复杂度为 。( )
正确答案正确
全为 `a` 时每次成功匹配后 `i` 前进约 ,只有约 次完整比较,总代价 ;追加内容总量约 ,仍为 。
针对下列输入数据,程序的输出为( )。
National Olympiad in Informatics A154
A
C正确答案B
程序把 `National Olympiad in Informatics A154` 中所有 `a` 或 `A` 都替换为大写 `C`,得到 `NCtionCl OlympiCd in InformCtics C154`。
针对下列输入数据,程序的输出为( )。
abaabaaabaaaabaaaaabababab
aa
ab正确答案C
程序从左到右寻找 `aa`,成功后写入 `ab` 并跳过两个原字符;按此规则处理得到 `ababbababababbabababababab`。
1 | #include<iostream>
2 | using namespace std;
3 | int a[100005], b[100005], n, m;
4 | void very_quick_sort(int l, int r, int p, int q){
5 | if(l >= r || p > q){ // ①
6 | return;
7 | }
8 | int mid = (l + r) / 2;
9 | int p0 = p - 1;
10 | int q0 = q + 1;
11 | for(int i = p; i <= q; i++){
12 | if(a[i] > mid) b[++p0] = a[i];
13 | else b[--q0] = a[i];
14 | }
15 | for(int i = p; i <= q; i++)
16 | a[i] = b[i];
17 | very_quick_sort(mid + 1, r, p, p0);
18 | very_quick_sort(l, mid, q0, q);
19 | }
20 | int main(){
21 | cin >> n >> m;
22 | for(int i = 1; i <= n; i++)
23 | cin >> a[i];
24 | very_quick_sort(1, m, 1, n);
25 | // ②
26 | for(int i = 1; i <= n; i++)
27 | cout << a[i] << " ";
28 | cout << endl;
29 | return 0;
30 | }保证输入的 不超过 , 不超过 ,且 。完成下面的判断题和单选题。
(1 分)上述代码实现了一种排序算法,可以将 `a` 数组按照从小到大的顺序排序。( )
正确答案错误
划分时大于 `mid` 的值写到左侧,小于等于 `mid` 的值写到右侧,递归后数组按从大到小排列,因此题述“从小到大”错误。
如果在程序开始之前向 `b` 数组里写入数据(保证不会发生数组越界),则上述代码的输出不会发生变化。( )
正确答案正确
每次处理非空区间 `p..q` 时,循环都会把 `b[p..q]` 全部重新写入,再复制回 `a`,所以 `b` 的初始内容不会影响输出。
若 ,存在某种数据构造方式,使得上述代码运行的时间复杂度为 ,这是因为算法本身是对快速排序的改进,但是这种改进不能避免由于对数组的划分不够均等而在极端数据下导致复杂度发生退化。( )
正确答案错误
每次递归都把值域 `[l,r]` 对半,深度为 O(log m),每层总共扫描 个元素;划分不会因数据偏斜退化为 。
如果将 ① 处的 `l >= r` 条件删除(同时删除 `||` 使得程序能正常编译运行,下同),程序的时间复杂度不会发生变化;而将 `p > q` 条件删除,程序在某些数据下的运行效率将会明显降低。( )
正确答案错误
删除 `l>=r` 后,值域缩到单点且区间非空时会反复递归同一状态,不能正常终止,所以“复杂度不会变化”这一前提已经错误。
不认为 同阶,即可能出现 远大于 或者 远大于 的情况。则该程序的最坏时间复杂度为( )。
正确答案D
递归深度为 Θ(log m),每一层所有区间合计处理 个元素,因此最坏时间复杂度是 Θ(n log m)。
若输入数据为:
10 10
10 4 5 2 2 3 1 5 8 3那么在程序执行到 ② 位置时,`b` 数组内的值为( )。
[10,8,3,5,1,3,2,2,5,4][3,5,1,3,2,2,5,4,10,8][10,8,5,5,4,3,3,2,2,1][1,2,2,3,3,4,5,5,8,10]正确答案C
按值域递归完成后,数组被排成降序;最后一次划分写入 `b` 的完整结果为 `[10,8,5,5,4,3,3,2,2,1]`。
1 | #include <iostream>
2 | #include <vector>
3 | #include <algorithm>
4 | using namespace std;
5 | const int mod = 1000000000 + 7;
6 | int w0[100005];
7 | int w1[100005];
8 | int w2[100005];
9 | int n, m, k, f[100005], d[100005], id[100005];
10 | vector <int> e[100005];
11 | void dfs(int u, int fa){
12 | d[u] = d[fa] + 1;
13 | f[u] = fa;
14 |
15 | for(auto &v : e[u]) if(v != fa){
16 | dfs(v, u);
17 | }
18 | }
19 | bool cmp(int a, int b){
20 | return d[a] < d[b];
21 | }
22 | int main(){
23 | cin >> n >> m >> k;
24 | for(int i = 2; i <= n; i++){
25 | int u, v;
26 | cin >> u >> v;
27 | e[u].push_back(v);
28 | e[v].push_back(u);
29 | }
30 | dfs(1, 0); // ①
31 | for(int i = 1; i <= m; i++){
32 | int x, w;
33 | cin >> x >> w;
34 | w1[x] = (w1[x] + w) % mod;
35 | w2[x] = (w2[x] + w) % mod;
36 | }
37 | for(int i = 1; i <= n; i++)
38 | id[i] = i;
39 | sort(id + 1, id + 1 + n, cmp);
40 | for(int i = 1; i <= k; i++){
41 | for(int j = n; j >= 1; j--){
42 | int x = id[j];
43 | for(auto &y : e[x]) if(y != f[x]){
44 | w1[y] = (w1[y] + w1[x]) % mod;
45 | }
46 | w1[x] = 0;
47 | }
48 | for(int x = 1; x <= n; x++)
49 | w1[x] = (w1[x] - w0[x] + mod) % mod,
50 | w0[x] = 0;
51 | for(int j = 1; j <= n; j++){ // ②
52 | int x = id[j];
53 | if(f[x]){
54 | w1[f[x]] = (w1[f[x]] + w2[x]) % mod;
55 | w2[f[x]] = (w2[f[x]] + w2[x]) % mod;
56 | w0[x] = (w0[x] + w2[x]) % mod;
57 | w2[x] = 0;
58 | }
59 | }
60 | }
61 | for(int i = 1; i <= n; i++)
62 | cout << w1[i] << " ";
63 | return 0;
64 | }保证输入的 不超过 , 不超过 20,且 ,。完成下面的判断题和单选题。
如果更改 ① 处 `dfs(1, 0)` 为 `dfs(n, 0)`,则输出结果可能有变化。( )
正确答案错误
程序的父子方向只服务于分层转移,向下传播、向上汇总和 `w0` 去重共同计算与根无关的树上距离信息;改根不会改变输出,因此“可能变化”错误。
(1 分)如果 `k=n`,那么输出结果均为 0。( )
正确答案正确
一棵有 个结点的树中任意两点距离至多 ;程序在第 `k` 轮保留距离为 `k` 的贡献,所以 `k=n` 时所有结果均为 0。
如果更改 ② 处 `for(int j=1; j<=n; j++)` 为 `for(int j=n; j>=1; j--)`,那么对于任意合法的输入数据,更改前后程序的输出均相同。( )
正确答案错误
原循环按深度从浅到深处理,使 `w2` 每轮只向父亲移动一层;反向后某个值可能在同一轮连续传到祖先多层,输出会改变。
(2 分)该程序的时间复杂度为( )。
正确答案B
按题目答案的计量口径,读入和建树为 Θ(n+m),每轮三组遍历均为 Θ(n),共 `k` 轮得到 Θ(nk+m),选择 B。
对于以下的输入数据,输出结果为( )。
5 2 1
1 2
2 3
3 4
3 5
1 5
3 20 7 0 2 22 7 2 3 25 2 1 1 10 2 1 1 2正确答案A
初始权值位于结点 1 的 5 和结点 3 的 2;完整执行一轮向下、去重和向上转移后,`w1[1..5]` 为 `0 7 0 2 2`。
对于以下的输入数据,输出结果为( )。
9 9 2
1 2
1 7
2 3
2 4
7 8
4 5
4 6
8 9
1 1
2 10
3 100
4 1000
5 10000
6 100000
7 1000000
8 10000000
9 10000000010001100 1110000 1001 101 100010 10010 100000010 1 10000000 0 1 1 10 10 0 1 100000011001110 1111100 1001 110101 100010 10010 110000010 100000001 100000011001111 1111112 1121 111121 112010 112010 111000012 112000001 121000000正确答案A
按深度顺序执行两轮转移,第一轮 `w1` 为 `1000010 1101 10 110010 1000 1000 10000001 101000000 10000000`,第二轮得到选项 A。
(序列问题)给定序列 ,求有多少对 满足 。测试数据满足 ,。
提示:对于任意的 ,可以发现 或 能对答案产生 1 的贡献,因此只需要用总的对数减去 的 数量,就能得到答案。试补全程序。
#include<bits/stdc++.h>
using namespace std;
const int Maxn = ①;
int n, a[Maxn];
long long s[Maxn], ans;
bool check(int l, int r) {
if (s[r] - s[l - 1] == ②) return true;
return false;
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
sort(a + 1, a + n + 1);
for (int i = 1; i <= n; i++)
s[i] = s[i - 1] + a[i];
ans = ③;
for (int i = 1; i <= n;) {
int l = i, r = n, pos = n;
while (④) {
int mid = (l + r) >> 1;
if(check(l, mid))
l = mid + 1, pos = mid;
else
r = mid - 1;
}
ans -= ⑤;
i = pos + 1;
}
cout << ans;
}① 处应填( )。
1e6 + 710000001e61000000000000正确答案A
数组使用 1 到 `n` 的下标且 ,容量必须大于 ;`1e6 + 7` 留出边界空间,选择 A。
② 处应填( )。
(r - l + 1) * a[l]s[r] - s[l - 1]1LL * (r - l + 1) * a[l]a[1] = 0正确答案C
排序后区间 `[l,r]` 全部等于 `a[l]` 当且仅当区间和等于 `1LL*(r-l+1)*a[l]`;`1LL` 防止乘法在 `int` 中溢出。
③ 处应填( )。
n * (n + 1) / 21LL * n * (n + 1) / 2n * (n - 1) / 21LL * n * (n - 1) / 2正确答案D
不同下标组成的无序对总数为 ,且 可达 ,应写成 `1LL * n * (n - 1) / 2`。
④ 处应填( )。
l < rl <= rl <= r + 1l > r正确答案B
代码要检查包含单个元素的最后区间,标准闭区间二分条件是 `l <= r`,对应 B。
⑤ 处应填( )。
(pos - l + 1) * (pos - l) / 21LL * (pos - l + 1) * (pos - l) / 2(pos - i + 1) * (pos - i) / 21LL * (pos - i + 1) * (pos - i) / 2正确答案D
当前相等段从固定起点 `i` 到 `pos`,长度为 `pos-i+1`,需减去 ;用 `1LL` 防溢出,故选择 D。
(异或和)给定序列 ,求其所有子区间异或和的和。某个区间的异或和是将这个区间内的所有数字分别进行异或计算。其中 ,。
提示:对每一位独立计算,对右端点扫描线,并用异或前缀和辅助统计。试补全程序。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 7;
int n, a[N], cnt[2];
long long ans;
int main () {
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i], ①;
for (int bit = ②; ③; bit--) {
cnt[0] = cnt[1] = 0;
for (int i = 0; i <= n; i++) {
cnt[④]++;
ans += 1LL * ⑤;
}
}
cout << ans;
}① 处应填( )。
a[i - 1] ^= a[i]a[i] ^= a[i - 1]a[i - 1] += a[i]a[i] += a[i - 1]正确答案B
扫描输入时应令 `a[i] ^= a[i-1]`,使 `a[i]` 变成前 `i` 个数的异或前缀和,因此选择 B。
② 处应填( )。
n - 129nn + 1正确答案B
,只需枚举第 29 位到第 0 位,所以循环初值为 29。
③ 处应填( )。
bitbit >= nbit - 1~bit正确答案D
`bit` 从 29 递减时,`~bit` 在 `bit>=0` 时非零;减到 -1 后 `~(-1)=0`,循环恰好结束,因此选择 D。
④ 处应填( )。
(a[i] >> bit) & 1a[i] & (1 << bit)a[i] & (1LL << bit)(a[i] >> bit) ^ 1正确答案A
计数数组下标只能是 0 或 1,表达式 `(a[i] >> bit) & 1` 正好提取第 `bit` 位,选择 A。
⑤ 处应填( )。
cnt[(a[i] >> bit) & 1] << icnt[(a[i] >> bit) & 1 ^ 1] << bitcnt[(a[i] >> bit) & 1] << bitcnt[(a[i] >> bit) & 1 ^ 1] << i正确答案B
当前前缀第 `bit` 位为 `v` 时,只有此前位为 `v^1` 的前缀能产生该位为 1 的区间;贡献为 `cnt[v ^ 1] << bit`,对应 B。