试卷解析 · 理解每一步
提高组 CSP-S 2026 初赛模拟卷 1
CSPS-2026-R1-Mock-1
试卷解析总览,可直接查看每题答案与解析。
第 1 题(单选题,2 分)
在 NOI Linux 中,使用 g++ 编译 C++ 程序时,如果要开启所有警告信息,应该使用选项()。
正确答案D
解析详情
-Wall 选项开启大部分警告信息,帮助发现潜在问题。
第 2 题(单选题,2 分)
如果 x 是正整数,则表达式 成立当且仅当()。
正确答案B
解析详情
x & (x-1) 的作用是消除 x 最低位的 1,如果结果为 0,说明 x 只有一个 1,即 2 的幂。注意,若不限制 x 为正数,则 x=0 和 0x80000000(也就是 -2147483648)也满足此式。
第 3 题(单选题,2 分)
操作系统的主要功能不包括()。
正确答案C
解析详情
数据库管理是数据库系统的功能,不是操作系统的核心功能。
第 4 题(单选题,2 分)
以下问题中,贪心算法不能得到最优解的是()。
正确答案C
解析详情
对于 0-1 背包问题,贪心算法不能保证得到最优解,需用动态规划。
第 5 题(单选题,2 分)
采用长度为7的哈希表,哈希函数 ,采用线性探查法解决冲突。依次插入关键字 19,26,35,48,55,最后 55 存放在位置()。
正确答案C
解析详情
, 冲突 , , 冲突 冲突 , 冲突 冲突 冲突 。
第 6 题(单选题,2 分)
以下关于欧拉图的叙述中,正确的是()。
正确答案A
解析详情
欧拉图的定义即存在欧拉回路的图。选项 B,哈密顿回路和欧拉回路不是一个概念;选项 C 还要加上连通条件;选项 D 描述的是欧拉路径,非欧拉回路。
第 7 题(单选题,2 分)
以下给定代码段的时间复杂度为()。
for (int i = 1; i <= n; i += 2) {
for (int j = 1; j <= i; j++) {
// O(1) 操作
}
}正确答案A
解析详情
内层循环次数为 。
第 8 题(单选题,2 分)
定义函数 ,其中 , ,则 的值为()。
正确答案C
解析详情
,,。
第 9 题(单选题,2 分)
对长度为 n 的有序数组进行二分查找,()不是必须满足的条件。
正确答案D
解析详情
从大到小排序也可以,二分查找只需要有单调性即可。选项 A 和 C 稍有一些含糊,相比之下选项 D 更明显。
第 10 题(单选题,2 分)
在模 11 运算中,7 的乘法逆元是()。
正确答案D
解析详情
最简单的方法是依次代入验证: 。正向方法,例如费马小定理: 。
第 11 题(单选题,2 分)
在 的网格中,起点为左上角的格子,终点为右下角的格子,每次可以移动到右方、下方、右下方的格子,则总共有( )种不同的路径。
正确答案D
解析详情
数据不大,可手算递推(标数法): , 。
第 12 题(单选题,2 分)
一个无向图至少需要 3 种颜色对顶点着色(才能保证任何一条边的两端不同色),当且仅当该图( )。
正确答案B
解析详情
二分图当且仅当可用 2 种颜色染色,也当且仅当无奇环,所以选项 B 正确;选项 C 需要顶点个数种颜色;选项 D 只是必要条件但不充分。
第 13 题(单选题,2 分)
在以下排序算法中,最坏情况下时间复杂度为 且是稳定排序的是()。
正确答案C
解析详情
归并排序在最坏情况下和平均情况下的时间复杂度均为 ,并且是稳定排序;快速排序最坏情况下的复杂度为 且不稳定;堆排序最坏情况下的复杂度是 ,但不稳定;希尔排序不稳定,其复杂度与所选增量序列有关,但都是多项式级,不会取到 。
第 14 题(单选题,2 分)
要交换两个整数 a 和 b 的值,不使用临时变量,最安全稳妥的办法是()。
正确答案B
解析详情
思想都是把两数通过可逆的某种运算组合在一起。选项 C 因为乘法溢出后无法再通过除法恢复,首先排除;选项 A 和 D 可能出现中间数值溢出,虽然严格说来其操作最后会“反溢出”回来,最终结果也对,但相比而言还是选项 B 最稳妥;选项 B 是位运算,每一位
是独立运算,不涉及进退位,所以是最保险的。
第 15 题(单选题,2 分)
已知哈夫曼树有 5 个叶节点,其权值分别为 1,3,5,7,9,该树的带权路径长度为()。
正确答案B
解析详情
构建过程:合并 1 + 3 = 4,合并 ,合并 ,合并 。
节点1(深度3)+ 节点3(深度3)+ 节点5(深度2)+ 节点7(深度2)+ 节点9(深度2),
WPL = 。
二、阅读程序(1)
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int n, m, k;
cin >> n >> m >> k;
vector<int> nums(n);
for (int i = 0; i < n; i++) cin >> nums[i]; // 保证输入的都是正整数
vector<vector<bool>> dp(k + 1, vector<bool>(m + 1, false));
dp[0][0] = true;
for (int idx = 0; idx < n; idx++) {
int num = nums[idx];
for (int i = k; i >= 1; i--) {
for (int j = m; j >= num; j--) {
if (dp[i - 1][j - num]) {
dp[i][j] = true;
}
}
}
}
if (dp[k][m]) {
cout << "Yes" << endl;
} else {
cout << "No" << endl;
}
return 0;
}第 16 题(判断题,2 分)
输入5 10 3 1 2 3 4 5时,输出结果为Yes。()
正确答案正确
解析详情
或 .
第 17 题(判断题,2 分)
如果 m < k,最终输出肯定是 No。 ()
正确答案正确
解析详情
因为输入的都是正整数,k个数之和至少为k。
第 18 题(判断题,2 分)
将 for (int i = k; i >= 1; i--) 改为正序遍历,程序结果不变。 ()
正确答案错误
解析详情
受滚动数组影响,计算 f[i] 时依赖 f[i-1],i 倒序时后者还是 idx-1 轮的值,这表示 num[idx] 只能用一次;如反序则表示每个数可以重复用,所以结果会变。
第 19 题(单选题,3 分)
dp[i,j] 的状态定义正确的是()。
正确答案C
解析详情
因为初始时只有 ,说明恰好用 i 个;选项 B,初始为 (对所有 j),但转移方程还是一样的。
第 20 题(单选题,4 分)
当 n > 0, k = 0 且 m = 0 时,下列说法中正确的是()。
正确答案A
解析详情
初始化条件 dp[0,0]=true,使用 0 个数可以组成和 0,因此当 k=0,m=0 时输出 Yes。
二、阅读程序(2)
#include <iostream>
using namespace std;
typedef long long ll;
ll fPow(ll a, ll b, ll mod) {
ll res = 1;
a %= mod;
while (b > 0) {
if (b & 1) res = (res * a) % mod;
a = (a * a) % mod;
b >>= 1;
}
return res;
}
int main() {
ll n, p;
cin >> n >> p;
if (n >= p) {
cout << 0 << endl;
return 0;
}
ll res = p - 1;
for (ll i = n + 1; i < p; i++)
res = (res * fPow(i, p - 2, p)) % p;
cout << res << endl;
return 0;
}第 21 题(判断题,2 分)
输入 n = 1, p = 5 时,输出结果为 4。 ()
正确答案错误
解析详情
1!。
第 22 题(判断题,2 分)
当 p 是合数时,程序仍然能正常运行。 ()
正确答案正确
解析详情
虽然合数不满足费马小定理,但程序运行不会有错,至少结果没有如上的数论意义。
第 23 题(判断题,2 分)
fPow(2,10,1000) 的返回值是 24. ()
正确答案正确
解析详情
1000 = 1024 \% 1000 = 24.
第 24 题(单选题,3 分)
如果输入 n = 0, p = 7, 程序的输出结果是()。
正确答案B
解析详情
0! % 7 = 1.
第 25 题(单选题,4 分)
如果p是素数,程序的时间复杂度是()。
正确答案B
解析详情
外层循环执行(p-n-1)次,每次循环调用 fPow 复杂度为 。
二、阅读程序(3)
#include <iostream>
using namespace std;
bool isPrime(int n) {
if (n < 2) return false;
if (n == 2 || n == 3) return true;
if ((n & 1) == 0) return false;
for (int i = 3; i * i <= n; i += 2)
if (n % i == 0) return false;
return true;
}
int main() {
int l, r;
cin >> l >> r;
int cnt = 0;
for (int i = l; i <= r; i++) {
if (isPrime(i)) {
int power2 = 1;
while (power2 < i) power2 <<= 1;
if ((power2 - i) <= 2 || (i - (power2 >> 1)) <= 2)
cnt++;
}
}
cout << cnt << endl;
return 0;
}第 26 题(判断题,2 分)
对于任意大于 2 的素数, 函数中的循环次数严格小于 。 ()
正确答案错误
解析详情
for (int i = 3; i * i <= n; i += 2) 从 3 开始,步长为 2, ,所以循环次数最大可以等于 。
第 27 题(判断题,2 分)
如果区间 [1, r] 内没有素数,程序输出 0。 ()
正确答案正确
解析详情
主函数的 if 不会成立,也就是 cnt 永远为 0。
第 28 题(判断题,2 分)
若输入 5 7,则程序输出 0。()
正确答案错误
解析详情
[5,7] 内有两个素数 5 和 7, , ,所以输出 2。
第 29 题(单选题,4 分)
输入531时,程序的输出结果为()。
正确答案C
解析详情
区间 中,满足条件的素数有 5, 7, 17, 31,共 4 个。
第 30 题(单选题,4 分)
该程序在最坏情况下的时间复杂度是()。
正确答案B
解析详情
复杂度由 isPrime() 调用和内层 while 计算 power2 两部分贡献。其中后者每次不超过 ,但只对素数的 i 有贡献。根据素数定理, 中素数个数为 ,所以求 power2 的总贡献为 。
isPrime() 的贡献要复杂一些。其中 i 是素数时,一次调用为 ,总贡献为 ;i 是合数时,贡献为 中所有合数的最小素因数之和,这个数值的严格量级是 ,具体推导比较复杂,可自行查阅资料。
严格说来,总复杂度是 ,但选项中没有(推导它也属超纲),那么最符合的就是选项 B,也就是高于但最接近严格结果的一项。
三、完善程序(1)
求解同余方程组: , ,输出 的最小正整数解,若无解则输出 -1。
#include <iostream>
#include <vector>
using namespace std;
typedef long long ll;
ll exgcd(①) {
if (②) {
x = 1;
y = 0;
return a;
}
ll g = exgcd(b, a % b, y, x);
y = y - a / b * x;
return g;
}
ll mod(ll a, ll b) {
return (a % b + b) % b;
}
ll crt(vector<ll> &a, vector<ll> &m) {
int n = a.size();
ll a1 = a[0], m1 = m[0];
for (int i = 1; i < n; i++) {
ll a2 = a[i], m2 = m[i];
ll x, y;
ll g = exgcd(m1, m2, x, y);
if (③) return -1;
ll p, q;
exgcd(m1 / g, m2 / g, p, q);
④;
ll x_val = (a1 + m1 * (a2 - a1) / g * p) % lcm;
a1 = mod(x_val, lcm);
m1 = lcm;
}
return ⑤;
}
int main() {
int n;
cin >> n;
vector<ll> a(n), m(n);
for (int i = 0; i < n; i++) // 表示一个方程 x ≡ a (mod m)
cin >> a[i] >> m[i];
ll res = crt(a, m);
if (res == -1) cout << -1 << endl;
else cout << res << endl;
return 0;
}第 31 题(单选题,3 分)
①处应填()。
int a, int b, int x, int yint a, int b, int &x, int &yll a, ll b, ll x, ll yll a, ll b, ll &x, ll &y正确答案D
解析详情
根据代码整体分析,参数类型为 11,exgcd 是实现扩展欧几里得算法,在计算 a 和 b 的最大公约数的同时,给出一组 ax - by = gcd(a, b) 的可行解,保存在 x 和 y 的变量中。exgcd 只返回最大公约数,故 x 和 y 应该是保存在传入的参数中,参数类型为引用传递。此处原理如下。要求 , 的公约数为 g,则 a_1, a_2 必须模 g 同余。在此条件下,等价于 ,先用 exgcd 求出 的解 p 和 q,再令 即可。
第 32 题(单选题,3 分)
②处应填()。
a == 0b == 0x == 0y == 0正确答案B
解析详情
辗转相除法,除数为0时,a为最大公约数,同时x=1,y=0是一组解。
第 33 题(单选题,3 分)
③处应填()。
a1 % g != 0a2 % g != 0a1 % g == a2 % g(a2 - a1) % g != 0正确答案D
解析详情
判无解,如果两个余数的差不能被最大公约数整除,说明要求模 g 意义下 x 同时与 a1,a2 同余,这不可能。
第 34 题(单选题,3 分)
④ 处应填( )。
ll lcm = m1 * m2ll lcm = m1 / g * m2ll lcm = g / m1 * m2ll lcm = m1 * g / m2正确答案B
解析详情
逐个数加入求最小公倍数,等于乘积除最大公约数。
第 35 题(单选题,3 分)
⑤处应填()。
a1m1-1lcm正确答案A
解析详情
最后合并成一个方程,x=a1 就是答案。
三、完善程序(2)
给定一棵有 n 个节点的树,树以节点 1 为根。程序使用倍增算法求解树上任意两个节点的最近公共祖先(LCA)。最近公共祖先是指两个节点在树上的公共祖先中深度最大的那个节点。当有多组问询时,按照问询的顺序,依次输出每个问询的答案。
#include <bits/stdc++.h>
using namespace std;
const int N = 200009;
vector<int> to[N];
int L, n, q, p[N][20];
int tI[N], tO[N], timer;
void dfs(int u, int fa) {
tI[u] = ++timer;
p[u][0] = fa;
for (int i = 1; i <= L; ++i) p[u][i] = ①;
for (int i = 0; i < to[u].size(); ++i)
if (to[u][i] != fa) dfs(to[u][i], u);
tO[u] = ++timer;
}
bool up(int u, int v) {
return ②;
}
int lca(int u, int v) {
if (up(u, v)) return u;
if (up(v, u)) return v;
for (int i = L; i >= 0; --i)
if (③) u = p[u][i];
return ④;
}
int main() {
cin >> n;
for (int i = 1; i <= n - 1; i++) {
int u, v;
cin >> u >> v;
to[u].push_back(v);
to[v].push_back(u);
}
L = 1;
while (⑤) ++L;
dfs(1, 0);
cin >> q;
for (int i = 1; i <= q; i++) {
int x, y;
cin >> x >> y;
cout << lca(x, y) << endl;
}
return 0;
}第 36 题(单选题,3 分)
①处应填()。
p[p[u][i]][i-1]p[p[u][i-1]][i-1]p[p[fa][i]][i-1]p[p[fa][i-1]][i-1]正确答案B
解析详情
u 的 级祖先等于节点 u 的 级祖先的 级祖先。
第 37 题(单选题,3 分)
② 处应填()。
u && tI[u] <= tI[v] && tO[v] <= tO[u]u || tI[u] <= tI[v] && tO[v] <= tO[u]!u && tI[u] <= tI[v] && tO[v] <= tO[u]!u || tI[u] <= tI[v] && tO[v] <= tO[u]正确答案D
解析详情
判断 u 是不是 v 的祖先。需要两个条件:一是 u 为 0(根节点的父节点)时直接返回 true;二是 u 的 DFS 进入时间小于等于 v 的进入时间,且 v 的离开时间小于等于 u 的离开时间。
第 38 题(单选题,3 分)
③处应填()。
!up(p[u][i], v)up(p[u][i], v)!up(u, p[v][i])up(u, p[v][i])正确答案A
解析详情
如果 u 的 级祖先不是 v 的祖先,说明 LCA 在更上层,此时可以将 u 向上跳转到 级祖先。
第 39 题(单选题,3 分)
④处应填()。
uvp[u] [0]p[v] [0]正确答案D
解析详情
循环结束后,u 的位置应该是 LCA 的直系子节点,因此 LCA 就是 u 的父节点 p[u][0]。注意如果开始时 u, v 有直系关系,这时候就应该返回。
第 40 题(单选题,3 分)
⑤ 处应填()。
(1 << L) <= nL <= n(1 << L) < nL < n正确答案A
解析详情
L 表示倍增的最大层级,需要满足 ,使用 条件表示 L 还要变大。注意这个地方实际上也不需要卡这么死,一般根据最大的 n 值手设例如 L=20 就行了,因为 超过深度时,f[u,i] 自然为 0,所以没有影响。