夜雨聆风学习资料网

ARTICLE · 1048697

2026 CSP-S 初赛真题及答案解析(完整版)|43题逐题详解

2026 CSP-S 初赛真题及答案解析(完整版)|43题逐题详解

2026 CSP-S 初赛真题及答案解析(完整版)|43 题逐题详解

2026 年 CSP-S 提高组第一轮认证真题来了!本文按单项选择题、阅读程序、完善程序三大题型,逐题给出完整题目、答案与详细解析,覆盖 43 道小题。S 组难度显著高于入门组,考点横跨位运算、哈夫曼、区间 DP、树状数组、Catalan 数、KMP、快速幂、CRC 校验、ST 表、树的直径、二分图、格雷码……建议收藏后慢慢消化。

获取 2026 CSP-S初赛真题及解析.pdf

请关注状元编程公众号,回复  2026csp-s


一、单项选择题(共 15 题,每题 2 分,共 30 分)

第 1 题:执行下列代码后,cnt 的值是( )。

int x = 2026, cnt = 0;while (x) {    x &= x - 1;    cnt++;}

A. 6  B. 7  C. 11  D. 8

答案:D

x &= x - 1 的作用是消掉二进制里最低位的那个 1,所以循环次数 = 2026 的二进制中 1 的个数。2026 = 2¹⁰+2⁹+2⁸+2⁷+2⁶+2⁵+2³+2¹,共 8 个 1。

考点:位运算 x & (x-1) 消最低位 1、数二进制中 1 的个数。

第 2 题:用权值 {1, 2, 3, 4, 5, 6, 7, 8} 构造哈夫曼树,其带权路径长度是( )。

A. 108  B. 96  C. 99  D. 102

答案:D

每次合并权值最小的两个,累加所有合并代价:3+6+9+12+15+21+36 = 102。带权路径长度 WPL 等于所有合并代价之和。

考点:哈夫曼树的构造与 WPL 计算。

第 3 题:把 1 到 1000 的所有整数按十进制写出,数字 "1" 总共出现了多少次( )。

A. 300  B. 271  C. 301  D. 320

答案:C

把 0~999 都补成三位数,每一位出现 1 的次数都是 100 次,三位合计 300;再加 1000 里的那个 1,共 301 次。

考点:数位统计。

第 4 题:将 5 封信随机装入 5 个写好地址的信封(每封一个),恰好有 2 封信装对的方案数是( )。

A. 44  B. 24  C. 10  D. 20

答案:D

先选哪 2 封装对:C(5,2) = 10;剩下 3 封必须全部装错(错排)D(3) = 2。10×2 = 20

考点:组合计数 + 错排。

第 5 题:3²⁰²⁶ mod 100 的值是( )。

A. 29  B. 9  C. 43  D. 81

答案:A

3 的幂模 100 周期为 20(3²⁰ 末两位是 01)。2026 mod 20 = 6,3⁶ = 729,末两位 29

考点:模运算的周期性 / 快速幂。

第 6 题:有 5 堆石子排成一行,重量依次为 4, 1, 3, 2, 5。每次只能合并相邻两堆,代价为两堆重量之和。合并成一堆的最小总代价是( )。

A. 36  B. 35  C. 34  D. 33

答案:C

只能合并相邻两堆,是区间 DP(不是哈夫曼):dp[i][j] = min{ dp[i][k] + dp[k+1][j] } + 区间总重量,按区间长度从小到大推,得 dp[1][5] = 34

考点:区间 DP(石子合并)。

第 7 题:树状数组维护长度 n=16 的序列,查询前缀和 sum(11) 与单点修改 add(3, x) 分别需要访问多少个下标( )。

A. 3 和 4  B. 4 和 4  C. 3 和 5  D. 4 和 3

答案:A

查询前缀和不断减 lowbit:11→10→8→0,访问 3 个;单点修改不断加 lowbit:3→4→8→16,访问 4 个。查询递减、修改递增

考点:树状数组(Fenwick)的实现细节。

第 8 题:有向无环图 G 顶点 {1,2,3,4},边 {(1,2), (1,3)},顶点 4 与任何顶点均不相邻。该图不同的拓扑序共有( )。

A. 12  B. 8  C. 4  D. 6

答案:B

唯一约束是 1 排在 2、3 前,4 不受限。4 个数全排列 24 种,"1 在 2、3 前"概率 1/3,故 24÷3 = 8

考点:拓扑排序计数。

第 9 题:某分治算法满足 T(n) = T(n/3) + T(2n/3) + Θ(n),T(1) = O(1),则 T(n) 是( )。

A. Θ(n log n)  B. Θ(n²)  C. Θ(n^1.5)  D. Θ(n)

答案:A

两个子问题规模不同,主定理不适用,画递归树:每层合并代价 Θ(n),深度由最慢缩小的分支决定(约 log_{3/2} n),总代价 Θ(n log n)。

考点:递归树分析分治复杂度。

第 10 题:无根树 9 个结点,边集 {(1,2),(1,3),(2,4),(2,5),(3,6),(6,7),(7,8),(5,9)},该树的直径(以边数计)与重心分别是( )。

A. 直径 6,重心 3  B. 直径 7,重心 2  C. 直径 8,重心 1  D. 直径 7,重心 1

答案:D

最长路径 9-5-2-1-3-6-7-8,共 7 条边。重心要逐个删点比较最大连通块:删 1 后最大连通块 4(最小),故重心为结点 1。

考点:树的直径、树的重心。

第 11 题:有向图缩点后得到的 DAG 含 6 个顶点,入度为 0 的有 3 个,出度为 0 的有 4 个。为使原图变强连通,至少需添加( )条有向边。

A. 7  B. 6  C. 4  D. 3

答案:C

把 DAG 变强连通,最少加边数 = max(入度为 0 的点数, 出度为 0 的点数) = max(3, 4) = 4

考点:强连通分量 / 缩点后加边。

第 12 题:含 6 个结点的不同形态的二叉树共有多少棵(结点不带标号,区分左右子树)( )。

A. 42  B. 429  C. 132  D. 720

答案:C

n 个结点不同形态二叉树 = 第 n 个 Catalan 数:C(12,6)/7 = 924/7 = 132

考点:Catalan 数。

第 13 题:字符串 s = "ababaabab",其所有既是真前缀又是真后缀的非空子串长度之和是( )。

A. 4  B. 6  C. 7  D. 5

答案:B

沿 KMP 失配链取所有 border:π[9]=4("abab"),π[4]=2("ab"),π[2]=0 结束。长度和 = 4+2 = 6

考点:KMP 前缀函数(border)。

第 14 题:用归并排序统计逆序对,合并时 if (a[i] <= a[j]) 取左半,否则 ans += mid - i + 1。若把条件改成 a[i] < a[j],则 ans 统计的是( )。

A. 完全不变  B. 变为原来的两倍  C. 变为满足 i<j 且 a[i]>=a[j] 的数对个数  D. 变为原来的一半

答案:C

原条件统计 a[i] > a[j](严格逆序对)。改成 < 后,a[i]==a[j] 也落入 else 被累加,统计范围扩大到 a[i] ≥ a[j] 的数对个数。

考点:归并排序求逆序对、相等元素处理。

第 15 题:执行 power(2, 100, 1000),返回值是( )。

longlongpower(longlong a, longlong b, longlong p){longlong r = 1 % p;while (b) {if (b & 1) r = r * a % p;        a = a * a % p;        b >>= 1;    }return r;}

A. 576  B. 376  C. 976  D. 176

答案:B

快速幂求 2¹⁰⁰ mod 1000。用 CRT:2¹⁰⁰ mod 8 = 0,2¹⁰⁰ mod 125 = 1(欧拉定理 φ(125)=100)。解得 x ≡ 0 (mod 8) 且 x ≡ 1 (mod 125),在 0~999 内 x = 376

考点:快速幂、中国剩余定理。


二、阅读程序(3 大题,共 40 分)

阅读程序(一):二进制多项式模 2 除法(CRC 校验)

#include<iostream>#include<string>usingnamespace std;int a[100];string s;int gen[13] = {1100000001111};intmain(){    cin >> s;for (int i = 0; i < 32; ++i) a[i] = s[i] - '0';for (int i = 32; i < 44; ++i) a[i] = 0;for (int i = 0; i < 32; ++i) {if (a[i] == 0continue;for (int j = 0; j < 13; ++j) a[i + j] ^= gen[j];    }for (int i = 32; i < 44; ++i) cout << a[i];    cout << endl;return0;}

程序把 32 位输入串当成二进制数,后面补 12 个 0,用 13 位 gen = 1100000001111 做模 2 除法(^ 即不进位加法),输出 12 位余数——这是 CRC 校验思路。

第 16 题(判断):当输入为 32 个 '0' 时,程序输出 12 个 0。( )

答案:√。a[0..31] 全 0,每一轮都 continue,不做任何异或,a[32..43] 保持 0。

第 17 题(判断):程序运行结束后,数组 a 中下标 0 到 31 的元素一定全部为 0。( )

答案:√。每位要么是 0(continue 不变),要么是 1(异或 gen[0]=1 变 0),处理完必全 0。

第 18 题(判断):若将为 a[32] 到 a[43] 补 0 的循环删除,会改变程序输出结果。( )

答案:×。a 是全局数组,默认初值就是 0,删不删补零结果相同。

第 19 题(单选):关于第 6 行定义的数组 gen,下列说法正确的是( )。

A. gen 共有 12 个元素,表示 12 位除数  B. gen 共有 13 个元素,表示 13 位被除数  C. gen 共有 13 个元素,其中 gen[0] 是除数的最高位  D. gen 共有 13 个元素,其中 gen[12] 是除数的最高位

答案:C。初始化列表共 13 个元素,表示除数(生成多项式),下标 0 对应最高位(从 a[i+0] 开始往高位异或)。

第 20 题(单选):该程序实现的功能,最准确的说法是( )。

A. 输出 M 与 1100000001111 按位异或的结果  B. 将 M 补 12 个 0 后对 1100000001111 做模 2 除法求余数并输出  C. 逐位取反输出  D. 统计 1 的个数用 12 位二进制输出

答案:B。"补 12 个 0"= 左移 12 位(乘 2¹²),用 13 位除数做不进位除法即模 2 除法,输出 12 位余数。

第 21 题(单选):若将 if (a[i] == 0) continue; 删除,说法正确的是( )。

A. 输出不变  B. 可能运行错误  C. 能正常输出 12 位串,但结果与输入 s 无关  D. 运行结束后 a[0] 一定为 0

答案:C。删掉 continue 后每一位都无条件执行固定异或,计算与输入脱钩,输出变成固定 12 位串(不会越界,下标最大 43)。


阅读程序(二):倍增 GCD(稀疏表 ST 表)

#include<iostream>usingnamespace std;int n, m, a[100007], L, R, lg[100007], i, j, t, dp[100007][25], pw[25];intgcd(int x, int y){if (y == 0return x;returngcd(y, x % y);}intmain(){    cin >> n >> m;for (i = 1; i <= n; i++) cin >> a[i];    t = 0;    pw[0] = 1;for (i = 1; i <= 24; i++) pw[i] = pw[i - 1] * 2;for (i = 1; i <= 100000; i++)if (pw[t + 1] > i) lg[i] = t;else t++, lg[i] = t;for (i = 1; i <= n; i++) dp[i][0] = a[i];for (j = 1; j <= lg[n]; j++)for (i = 1; i + pw[j] - 1 <= n; i++)            dp[i][j] = gcd(dp[i][j - 1], dp[i + pw[j - 1]][j - 1]);for (i = 1; i <= m; i++) {        cin >> L >> R;        cout << gcd(dp[L][lg[R-L+1]], dp[R-pw[lg[R-L+1]]+1][lg[R-L+1]]) << endl;    }return0;}

预处理 dp[i][j] = 从 a[i] 起连续 2^j 个数的 gcd,再用两个长 2^k 的区间拼起来覆盖查询区间——稀疏表把区间 gcd 查询做到 O(1)

第 22 题(判断):当 n=5、a={4,2,6,3,9},仅一次查询 L=2、R=5 时,输出为 1。( )

答案:√。a[2..5]={2,6,3,9},最大公约数为 1。

第 23 题(判断):当某次查询区间长度为 1(L=R)时,输出一定等于 a[L]。( )

答案:√。lg[1]=0,两个区间都退化成长度 1,dp[L][0] = a[L]。

第 24 题(判断):任意一次查询的输出结果一定不小于该区间内的最小值。( )

答案:×。输出是 gcd,gcd 一定不大于区间最小值。如 {4,6},最小值 4,但 gcd=2 < 4。

第 25 题(单选):对于 j≥1,dp[i][j] 保存的是( )。

A. 从 a[i] 起连续 j 个数的 gcd  B. 从 a[i] 起连续 2^j 个数的 gcd  C. a[i] 与 a[j] 的 gcd  D. 从 a[1] 到 a[i] 的 gcd

答案:B。转移把两段 2^(j-1) 区间拼成 2^j。

第 26 题(单选):若把一次 gcd 视为 O(1),建表过程的时间复杂度为( )。

A. Θ(n)  B. Θ(n log n)  C. Θ(n²)  D. Θ(mn)

答案:B。外层 j 到 lg[n](约 log n 层),内层 i 到 n,共 Θ(n log n)。

第 27 题(单选):设 x 为查询区间长度,使 lg[x]=5 的 x 取值范围是( )。

A. [16,31]  B. [17,32]  C. [32,63]  D. [33,64]

答案:C。lg[x]=⌊log₂x⌋,2⁵=32 ≤ x < 2⁶=64,即 [32,63]。


阅读程序(三):树上递推求最长路径(直径)

#include<iostream>usingnamespace std;int n, fa[100007], f[100007], ans;intmain(){    cin >> n;for (int i = 2; i <= n; ++i) cin >> fa[i];for (int i = n; i >= 2; --i) {if (f[fa[i]] + f[i] + 1 > ans)            ans = f[fa[i]] + f[i] + 1;if (f[i] + 1 > f[fa[i]])            f[fa[i]] = f[i] + 1;    }    cout << ans << endl;return0;}

倒序(i 从 n 到 2)遍历,f[] 记"从该结点向下能走多远",用 f[fa[i]]+f[i]+1 更新答案。因父结点编号 < 子结点编号,倒序保证了处理 i 时它的孩子都已处理完。

第 28 题(判断):当 n=5,fa[2..5]={1,2,3,4} 时,程序输出 4。( )

答案:√。对应一条 1-2-3-4-5 的链,最长路径 4 条边。

第 29 题(判断):程序输出前,f[1] 的值一定等于 ans 的值。( )

答案:×。星形树(fa={1,1,1,1})时 ans=2 而 f[1]=1,两者不等。

第 30 题(判断):将两个 if 语句的顺序交换后,程序的输出结果不受影响。( )

答案:×。原程序先用旧 f[fa[i]] 更新答案,再更新 f[fa[i]];交换后会先把 i 的贡献并入父结点,等于同一条路径算两次,结果变大。

第 31 题(单选):程序输出的 ans 表示的是( )。

A. 树中距离最远的两个结点之间路径经过的边数  B. 根到最远叶子的边数  C. 叶子结点个数  D. 所有父结点编号之和

答案:A。ans 是"经过某结点的两条最长向下路径之和"的最大值,即树的直径

第 32 题(单选):当 n=7,fa[2..7]={1,1,2,2,3,3} 时,输出为( )。

A. 2  B. 3  C. 4  D. 5

答案:C。树形:1 带 2、3;2 带 4、5;3 带 6、7。最长路径 4-2-1-3-6 共 4 条边。

第 33 题(单选):当 n=10,满足输出为 9 的合法输入种类数为( )。

A. 0  B. 9  C. 256  D. 512

答案:C。n=10 时合法输入共 9! = 362880 种,枚举后输出为 9 的共 256 种。


三、完善程序(2 大题,共 30 分)

完善程序(一):平衡路线

给定一张 n 个顶点 m 条边的无向图,每条边带符号 + 或 −。从 s 到 t 的路线(可重复经过点边)权值为 |n⁺ − n⁻|(+ 边数与 − 边数之差的绝对值)。求 s 到 t 的最小权值,不存在则输出 −1。

#include<iostream>constexprint N = 200005;constexprint M = 400005;int n, m, s, t;int h[N], e[M << 1], ne[M << 1], w[M << 1], idx;int q[N], d[N], c[N];voidadd(int a, int b, int z){    e[idx] = b; w[idx] = z; ne[idx] = h[a]; h[a] = idx++;}intmain(){    std::cin >> n >> m >> s >> t;for (int i = 1; i <= n; i++) h[i] = d[i] = c[i] = -1;for (int i = 0; i < m; i++) {int a, b; char op[2];        std::cin >> a >> b >> op;int z = ____①____;add(a, b, z); add(b, a, z);    }int hh = 0, tt = 0, p = 0, ng = 0, ok = 1;    q[tt++] = s;    d[s] = c[s] = 0;while (____②____) {int x = q[hh++];for (int i = h[x]; i != -1; i = ne[i]) {int y = e[i];if (w[i] > 0) p = 1;if (w[i] < 0) ng = 1;if (d[y] == -1) {                d[y] = ____③____;                c[y] = c[x] ^ 1;                q[tt++] = y;            } elseif (____④____) ok = 0;        }    }if (d[t] == -1) { std::cout << -1return0; }if (!p || !ng) { std::cout << d[t]; return0; }if (____⑤____) std::cout << 0;else std::cout << 1;return0;}

BFS 求跳数 d[],同时做二分图染色 c[],用 p、ng 记录是否出现过 +/− 边,最后分情况输出。

第 34 题:① 处应填( )。A. op[0]=='+'?0:1 B. op[0]=='+' C. op[0]=='+'?1:-1 D. op[0]=='-'?1:0 → 答案:C

'+' 边记权值 1,'−' 边记 −1。

第 35 题:② 处应填( )。A. hh<n B. tt<n C. hh<=tt D. hh<tt → 答案:D

队列元素在 q[hh..tt−1],循环条件是 hh < tt。

第 36 题:③ 处应填( )。A. d[y]+1 B. d[x]+1 C. d[x] D. d[x]-1 → 答案:B

d[y] 记从 s 到 y 的跳数,比 d[x] 多一跳。

第 37 题:④ 处应填( )。A. c[y]==c[x] B. w[i]==1 C. c[y]!=c[x] D. d[y]+1!=d[x] → 答案:A

遇到已访问的 y 且相邻同色 c[y]==c[x],说明存在奇环,非二分图,ok 置 0。

第 38 题:⑤ 处应填( )。A. ok && c[s]==c[t] B. ok && c[s]!=c[t] C. !ok || c[s]==c[t] D. !ok && c[s]!=c[t] → 答案:C

正负边都出现时答案只能是 0 或 1:!ok(有奇环)或 c[s]==c[t](路径长为偶)时取 0,否则取 1。


完善程序(二):标准答案(格雷码枚举)

n 名学生、m 道选择题(每题 A/B)。构造一份标准答案使 Σ|rᵢ − xᵢ| 最大(rᵢ 为学生 i 得分,xᵢ 为目标分)。n ≤ 20,m ≤ 300。

#include<cstdlib>#include<iostream>#include<string>#include<vector>usingnamespace std;typedeflonglong ll;typedefunsignedlonglong ull;intmain(){int n, m;    cin >> n >> m;vector<ll> x(n)c(n);for (int i = 0; i < n; i++) {        cin >> x[i];        c[i] = ____①____;    }vector<string> a(n);for (int i = 0; i < n; i++) cin >> a[i];vector<ints(n, -1);vector<ll> q(m, 0);    ll C = 0, S = 0;for (int i = 0; i < n; i++) {        C -= c[i];for (int j = 0; j < m; j++) {if (a[i][j] == 'A') q[j]--;else q[j]++;        }    }for (int j = 0; j < m; j++) S += abs(q[j]);    ll ans = C + S;    ull best = 0, lst = 0;for (ull mask = 1; mask < (1ULL << n); mask++) {        ull g = ____②____;        ull d = g ^ lst;int k = ____③____;        C -= ____④____;for (int j = 0; j < m; j++) {            ll old = q[j];int v = (a[k][j] == 'A' ? 1 : -1);            q[j] += 2ll * s[k] * v;            S += abs(q[j]) - abs(old);        }        s[k] = -s[k];if (C + S > ans) { ans = C + S; best = g; }        lst = g;    }for (int i = 0; i < n; i++) {if (best >> i & 1) s[i] = 1;else s[i] = -1;    }string res(m, 'A');for (int j = 0; j < m; j++) {        ll v = 0;for (int i = 0; i < n; i++) {if (a[i][j] == 'A') v += s[i];else v -= s[i];        }if (____⑤____) res[j] = 'A';else res[j] = 'B';    }    cout << res << endl;return0;}

把每个 |rᵢ−xᵢ| 用 max 展开后,目标函数拆成"每名学生独立选符号 sᵢ"+"每道题独立选符号"两部分;n ≤ 20 用格雷码遍历所有 2^n 个子集,每次翻转一个人、增量更新。

第 39 题:① 处应填( )。A. 2*x[i]-m B. -m+2*x[i]+1 C. m-2*x[i] D. m+2*x[i] → 答案:C

每名学生的常数项 c[i] = m − 2·x[i]。

第 40 题:② 处应填( )。A. mask|(mask>>1) B. mask^(mask>>1) C. mask&(mask>>1) D. mask^((mask>>1)+1) → 答案:B

格雷码 = mask ^ (mask >> 1),相邻两值只差一位,所以 d = g ^ lst 只有一位是 1。

第 41 题:③ 处应填( )。A. __builtin_ctzll(d)+1 B. __builtin_popcountll(d) C. __builtin_ctzll(g) D. __builtin_ctzll(d) → 答案:D

d 是 2 的幂,ctzll(d) 取末尾连续 0 的个数 = 变化位的下标 k。

第 42 题:④ 处应填( )。A. 2ll*s[k]*c[k] B. s[k]*c[k] C. 2ll*(s[k]-c[k]) D. 2ll*c[k] → 答案:A

翻转第 k 名学生符号,常数项变化量 = 2ll·s[k]·c[k]。

第 43 题:⑤ 处应填( )。A. v>=(n&1) B. v>(n&1) C. v+(n&1)>=0 D. v*(n&1)>=0 → 答案:A

v 是第 j 题所有学生带符号贡献之和,v≥(n&1) 统一了 n 为偶(v≥0)和 n 为奇(v≥1 即 v>0)两种情况。


四、参考答案速查表

题号
答案
题号
答案
题号
答案
1
D
16
31
A
2
D
17
32
C
3
C
18
×
33
C
4
D
19
C
34
C
5
A
20
B
35
D
6
C
21
C
36
B
7
A
22
37
A
8
B
23
38
C
9
A
24
×
39
C
10
D
25
B
40
B
11
C
26
B
41
D
12
C
27
C
42
A
13
B
28
43
A
14
C
29
×
15
B
30
×

本文基于 2026 年 CSP-S 第一轮认证真题整理,答案与解析由公开资料归纳,最终以 CCF 官方发布为准。祝各位同学提高组初赛顺利!

相关学习资料