2026 CSP-S 第一轮真题
第二部分·阅读程序第 1 题:《二进制除法》
答案是:
16:对✅️,17:对✅️,18:错❌️;19:C,20:B,21:C。
一、先认识题目中的“二进制除法”
程序中有一个数组:
int gen[13] = {1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1};它一共有 13 个元素:
下标: 0 1 2 3 4 5 6 7 8 9 10 11 12 gen: 1 1 0 0 0 0 0 0 0 1 1 1 1连起来就是:
1100000001111题目程序把输入的 32 位二进制字符串放进数组a,然后用gen进行一系列异或操作,最后输出a[32]到a[43]。
我们先记住两个重要知识:
异或
^:两个相同的二进制位异或得到 0,不同得到 1。模 2 除法:在二进制多项式除法中,减法可以用异或来完成,不需要借位。
二、逐段理解程序
1. 读入 32 位二进制字符串
cin >> s; for (int i = 0; i < 32; ++i) { a[i] = s[i] - '0'; }假设输入:
100100...cin >> s把整个字符串读入s。
接着:
a[i] = s[i] - '0';把字符'0'、'1'转换成整数0、1。
例如:
| 字符 | s[i] - '0' | 存入数组 |
|---|---|---|
'0' | 0 | a[i] = 0 |
'1' | 1 | a[i] = 1 |
所以,数组a[0]到a[31]保存输入的 32 个二进制位。
2. 给后面的 12 位补零
for (int i = 32; i < 44; ++i) { a[i] = 0; }循环让:
a[32], a[33], ..., a[43]全部变成 0。
一共是:
44−32=12
也就是在原来的 32 位二进制串后面补上 12 个 0。
为什么补 12 个?
因为gen有 13 位,而它的最高位对应a[i],所以当程序处理到a[31]时,仍然需要访问:
a[31 + 12]也就是:
a[43]因此数组必须准备到下标 43。
3. 关键循环:异或消除
for (int i = 0; i < 32; ++i) { if (a[i] == 0) continue; for (int j = 0; j < 13; ++j) { a[i + j] ^= gen[j]; } }这是整道题的核心。
我们先把异或的规则记住:
| 左边 | 右边 | 异或结果 |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
特别要记住:
所以,两个相同的位异或后会变成 0。
if (a[i] == 0) continue;是什么意思?
如果当前位a[i]是 0,就直接跳过本轮循环,不做后面的异或操作。
如果a[i]是 1,就把gen的 13 个二进制位依次与:
a[i], a[i+1], ..., a[i+12]进行异或。
由于gen[0] == 1,所以第一步一定会执行:
a[i] ^= 1;当a[i]原本是 1 时:
于是当前位就被消除了。
这和二进制长除法的思路非常相似:发现当前位是 1,就把除数对齐到这一位,用异或进行消除。
三、判断题 16:输入 32 个 0,输出是不是 12 个 0?
题目说:
当输入为 32 个
'0'时,程序输出 12 个 0。
我们来验证。
如果输入全是 0:
00000000000000000000000000000000那么:
a[0] = 0; a[1] = 0; ... a[31] = 0;补零后:
a[32] = 0; ... a[43] = 0;接下来进入主循环:
for (int i = 0; i < 32; ++i) { if (a[i] == 0) continue; ... }因为每个a[i]都是 0,所以每次都会执行:
continue;也就是说,内层异或循环一次都不会执行。
最后输出:
for (int i = 32; i < 44; ++i) { cout << a[i]; }输出的就是:
000000000000因此,判断题 16:
对(√)
四、判断题 17:结束后a[0]到a[31]一定全为 0 吗?
题目说:
程序运行结束后,数组
a中下标从 0 到 31 的元素一定全部为 0。
答案是:对。
为什么?
程序从左向右处理:
for (int i = 0; i < 32; ++i)对于每个位置i:
如果
a[i] == 0,就跳过;如果
a[i] == 1,就用gen异或消除它。
因为gen[0] == 1,所以当a[i] == 1时,第一位会变成:
1⊕1=01\oplus1=0
而内层循环只会修改:
a[i], a[i+1], ..., a[i+12]不会修改比i更靠前的位置。
因此,已经处理完的位置不会再被后面的循环改变。
可以把它想象成一排灯泡:
从左向右检查;
遇到亮着的灯泡,就执行一次操作把它熄灭;
后面的操作不会再碰已经检查过的左边灯泡。
最终:
a[0] = 0 a[1] = 0 ... a[31] = 0所以判断题 17:
对(√)
五、判断题 18:删除补零循环,会改变输出吗?
题目说:
若将第 12~14 行(为
a[32]到a[43]补 0 的循环)删除,会改变程序输出结果。
答案是:错。
这题有一个非常重要的 C++ 知识点。
程序中:
int a[100];这里的a是定义在函数外面的全局数组。
在 C++ 中,全局变量如果没有显式初始化,会自动进行零初始化。
也就是说,即使删除:
for (int i = 32; i < 44; ++i) { a[i] = 0; }数组a[32]到a[43]仍然会自动是 0。
因此,这个补零循环实际上是重复做了一次已经自动完成的事情。
删除它不会改变程序的输出。
所以判断题 18:
错(×)
记忆小卡片
全局数组:没有显式初始化时,元素会自动初始化为 0。
普通局部数组:没有显式初始化时,元素的值通常是不确定的,不能直接当成 0 使用。
六、单选题 19:gen数组表示什么?
题目问:
关于第 6 行定义的数组
gen,下列说法正确的是哪一个?
选项:
A.
gen共有 12 个元素,表示一个 12 位的除数B.
gen共有 13 个元素,表示一个 13 位的被除数C.
gen共有 13 个元素,其中gen[0]是除数的最高位D.
gen共有 13 个元素,其中gen[12]是除数的最高位
首先,数一数:
int gen[13] = {1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1};数组有 13 个元素,下标是:
0 到 12对应的二进制串是:
1100000001111程序把gen[0]与a[i]对齐,然后把gen[1]与a[i+1]对齐,依此类推。
所以gen[0]对应二进制串最左边的位,也就是最高位。
因此正确选项是:
C
七、单选题 20:程序实现了什么功能?
题目问:
该程序实现的功能,最准确的说法是哪个?
选项 B 的意思是:
将输入串视为 32 位二进制数 MM,在其后补 12 个 0,也就是计算 M×212M\times2^{12},再对它用
1100000001111作模 2 除法求余数,并输出 12 位余数。
我们前面已经分析了:
输入的 32 位串被存入数组;
后面补 12 个 0;
从左向右处理每一位;
当前位为 1 时,将除数对齐并异或;
最后输出下标 32 到 43 的 12 位结果。
这正是二进制模 2 长除法的过程。
因此,正确选项是:
B
为什么输出是 12 位?
除数是 13 位二进制数。
在普通长除法中,余数的位数小于除数的位数;这里除数是 13 位,所以余数最多是 12 位。
程序输出:
for (int i = 32; i < 44; ++i) cout << a[i];刚好输出 12 位。
八、单选题 21:删除continue后会发生什么?
题目问:
若将第 16 行
if (a[i] == 0) continue;删除,说法正确的是哪一个?
选项:
A. 程序的结果不会改变
B. 可能造成程序运行错误
C. 程序能够正常输出一个 12 位
'0'/'1'串,但是输出结果与输入的s无关D. 程序运行结束后,
a[0]的值一定为 0
正确选项是:
C
我们来仔细理解。
1. 原来有continue时
if (a[i] == 0) continue;如果当前位是 0,就跳过异或。
只有当前位为 1 时,才执行内层循环。
所以程序会根据输入的二进制串进行不同的操作。
2. 删除continue后
程序变成:
for (int i = 0; i < 32; ++i) { for (int j = 0; j < 13; ++j) { a[i + j] ^= gen[j]; } }这时,无论a[i]是 0 还是 1,都会执行异或。
而且循环次数完全固定:
i一定从 0 到 31;j一定从 0 到 12;每次都使用同一个固定的
gen。
因此,程序对数组执行的操作序列不再取决于输入字符串s。
虽然数组最初的前 32 位可能不同,但每次异或操作都按固定顺序执行,最终输出也会变成一个固定的 12 位串。
所以:
程序仍然可以正常输出;
输出长度仍然是 12 位;
输出结果与输入的
s无关。
因此,正确答案是C。
九、第一题的六道小题答案汇总
| 题号 | 正确答案 | 核心原因 |
|---|---|---|
| 16 | √ | 输入全 0,所有位都跳过异或,输出 12 个 0 |
| 17 | √ | 从左向右消除,每个处理过的位置最终为 0 |
| 18 | × | 全局数组会自动零初始化,删除补零循环不影响输出 |
| 19 | C | gen有 13 个元素,gen[0]是最高位 |
| 20 | B | 补 12 个 0 后进行模 2 除法,输出 12 位余数 |
| 21 | C | 删除continue后操作固定,输出不再依赖输入串 |
十、给同学们的三个重要知识点
知识点 1:异或可以用于二进制模 2 除法
a[i + j] ^= gen[j];异或的特点是:
0 ^ 0 = 0 0 ^ 1 = 1 1 ^ 0 = 1 1 ^ 1 = 0特别是1 ^ 1 = 0,它可以用来消除当前位。
知识点 2:continue会跳过本轮循环剩余部分
if (a[i] == 0) continue;当条件成立时,程序直接进入下一轮i循环,后面的内层循环就不会执行。
知识点 3:全局数组会自动初始化
int a[100];如果它是全局数组,没有显式初始化时,元素会自动变成 0。
这也是判断题 18 的关键。
这道题最核心的程序思想可以概括为:从左向右扫描二进制串,遇到 1 就用异或消除,最后留下 12 位余数。