简介:这份资源是第18届绍兴市少儿信息学竞赛复赛试题文档,面向备战NOIP系列赛事的小学高年级及初中信息学选手与辅导教师,用于熟悉复赛题型、检验算法与编程基本功。压缩包内共1个docx文件,约32KB,完整收录比赛题目说明、输入输出格式、样例与数据范围约定,并附有比赛目录结构与提交规范提醒。试题围绕“好朋友”“统计人口”“卫星”“粉刷匠”四道题展开,分别涉及数组与循环模拟、区间统计与数据结构、环形序列构造、行列染色计数等知识点,可帮助读者按题面还原评测要求,练习文件读写、边界处理与复杂度控制。目前已有815人学习下载,适合作为赛前模拟训练与知识点查漏补缺的参考材料。
1. 四道题就是一张地图:小学信息学复赛到底在考什么
很多人拿到一份少儿信息学竞赛的复赛真题,第一反应是“这不就是四道编程题吗”,然后埋头去写代码。但真正把这份卷子从头到尾拆一遍,会发现它其实是一张非常清晰的知识地图:四道题分别对应四个不同的能力层级,从最基础的枚举与条件判断,到前缀和优化,再到模拟与构造,最后是逆向思维加数据规模分析。这四道题放在一起,恰好覆盖了小学阶段信息学竞赛的核心考点——数组操作、循环控制、边界处理、时间复杂度的直觉判断,以及“读题比写代码更重要”这条血泪经验。
这份卷子适合两类人:一类是正在系统学习 C++ 或 Pascal、准备参加区市级竞赛的学生,可以用它做一次完整的自测;另一类是已经学过基础语法但总在比赛中翻车的选手,因为它的四道题几乎把小学组最常见的踩坑点都埋了一遍。150 分钟、四道题、每题 10 个测试点,这个配置本身就值得研究——它意味着平均每题只有 37 分钟左右,读题、建模、编码、调试全算进去,时间分配本身就是一道隐形的题。
2. 好朋友:枚举与距离计算的第一道门槛
2.1 题目在考什么
这道题的场景很直白:一条直线上有 n 套房,房号 1 到 n,相邻距离 10 米。小 A 住在 x 号房,小 B 想买一套离小 A 最近的房子,但有两个约束——不能买小 A 那套,且房价不能超过预算 m。价格 Pi 为 0 表示该房不可卖。求小 B 能买到的房子离小 A 家的最近距离。
核心逻辑拆开来看就三步:第一,遍历所有房号;第二,筛掉不可卖的(Pi = 0)和买不起的(Pi > m)以及小 A 自己那套(房号等于 x);第三,在剩下的房里找离 x 最近的,距离按 |房号差| × 10 计算。
2.2 关键实现与参数说明
用 C++ 写出来大概是这样:
#include <cstdio> #include <cstdlib> #include <climits> int main() { int n, x, m; scanf("%d %d %d", &n, &x, &m); int best = INT_MAX; // 初始化为无穷大,表示还没找到 for (int i = 1; i <= n; i++) { int p; scanf("%d", &p); if (i == x) continue; // 不能买小A自己的房子 if (p == 0) continue; // 不可卖 if (p > m) continue; // 买不起 int dist = abs(i - x) * 10; // 房号差乘以10米 if (dist < best) best = dist; } printf("%d\n", best); return 0; }这段代码里最容易被忽略的是best的初始值。如果初始化为 0,后面比较dist < best永远不成立,答案就错了。用INT_MAX是稳妥做法,但要注意题目保证至少有一套可买的房,所以循环结束后best一定被更新过。另一个细节是距离计算:题目说相邻房子距离 10 米,房号差为 d 时距离是 d × 10,不是 d。样例 1 里小 A 住 2 号,可买的最近是 4 号,房号差 2,距离 20,验证了这个公式。
2.3 复杂度与数据范围的关系
n 最大 100,O(n) 的遍历完全够用,甚至 O(n²) 都不会超时。但这道题的价值不在于复杂度,而在于“多条件筛选”的编码习惯:三个 continue 的顺序其实有讲究。如果先判断价格再判断房号,逻辑上没问题,但把i == x放在最前面可以少做一次价格读取后的判断——不过这里价格已经读进来了,所以顺序不影响正确性,只影响可读性。梳理下来会发现,这类题最稳的写法是“先排除所有不可能,再在可能里找最优”,而不是“边找边判断”,后者容易在边界上翻车。
3. 统计人口:前缀和从 O(nm) 到 O(n+m) 的跨越
3.1 暴力思路为什么不够
题目给 n 户人家的人口数,m 次询问,每次问“编号 x 到 y 的人都不在家,能核查到多少人口”。翻译一下就是:总和减去区间 [x, y] 的人口和。最直白的做法是每次询问都从 x 加到 y,再拿总和去减。n 和 m 最大都是 50000,最坏情况下每次询问区间覆盖整个数组,总操作次数 50000 × 50000 = 2.5 × 10⁹,1 秒时限下必然超时。
这就是这道题埋的第一个坑:数据范围里 30% 是 n, m ≤ 100,50% 是 n ≤ 1000、m ≤ 10000,只有 100% 才拉到 50000。如果只盯着小数据写暴力,能拿 50 分,但复赛要的是满分。
3.2 前缀和的标准解法
前缀和的核心就一句话:预处理一个数组pre[i]表示前 i 户的总人口,那么区间 [x, y] 的和等于pre[y] - pre[x-1]。每次询问 O(1),总复杂度 O(n + m)。
#include <cstdio> int a[50005]; long long pre[50005]; // 用long long防止累加溢出 int main() { int n, m; scanf("%d %d", &n, &m); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); pre[i] = pre[i - 1] + a[i]; // 前缀和递推 } long long total = pre[n]; for (int i = 0; i < m; i++) { int x, y; scanf("%d %d", &x, &y); long long away = pre[y] - pre[x - 1]; // 区间和 printf("%lld\n", total - away); } return 0; }几个参数要盯住:pre数组用long long,因为题目说答案不超过 2³¹−1,但前缀和累加过程中可能短暂超过 int 范围(虽然这题人口数不大,但养成习惯没坏处);pre[0]默认为 0,这是递推的起点,不能漏;区间和公式是pre[y] - pre[x-1],减的是x-1不是x,这是前缀和最经典的易错点。
3.3 输入输出效率的隐性考点
题目末尾专门提示“输入输出数据比较多,建议用 scanf、printf”。这不是客套话。m 最大 50000,如果用 cin/cout 且没关同步,在某些评测机上确实可能卡时限。小学组选手往往习惯用 cin,这道题就是一次明确的提醒:数据量上万的时候,输入输出方式要纳入考虑。另外输出格式是每行一个数,用printf("%lld\n", ...)比cout << ... << endl更稳,因为 endl 会强制刷新缓冲区,频繁调用有开销。
4. 卫星:环形模拟与构造题的思维转换
4.1 题目到底在求什么
这道题是四道里最绕的。它给一个 n,要求设计一个 1 到 n 的环形排列,使得按照特定规则接收信号时,接收顺序恰好是 1, 2, 3, …, n。规则是:先接收 1 号卫星;然后间隔 1 颗(跳过 1 颗未接收的),接收下一颗;再间隔 2 颗,接收下一颗;间隔数等于上一次接收到的卫星编号。已接收的卫星不再计数。
换句话说,题目不是让你模拟接收过程,而是让你反推排列。这是典型的构造题:正向规则已知,反向求初始状态。
4.2 从样例反推规律
看 n = 5 的样例输出:1 3 2 5 4。验证一下:环上顺序是 1→3→2→5→4→1。先收 1 号;间隔 1 颗未接收的,1 后面是 3(已收?不,3 还没收,但 1 刚收完,下一个是 3,间隔 1 颗意味着跳过 3?这里要仔细)。实际上规则里的“间隔 1 颗”指的是在未接收的卫星中数 1 颗跳过,然后收下一颗。按样例的环 1,3,2,5,4:收 1 后,未收的是 3,2,5,4,间隔 1 颗(跳过 3),收 2——但 2 在环上的位置是第三个,说明“间隔”是在环上按物理顺序数,跳过已收的不算。这个细节非常容易读错,也是这道题最大的理解门槛。
梳理下来,构造的规律是:第 k 步接收的卫星编号是 k,而它在环上的位置需要满足“从上一个接收位置出发,在剩余未接收卫星中数 k 颗(跳过 k 颗),下一颗就是 k+1 号”。反推时可以用一个动态数组维护剩余卫星,每次按规则插入。
4.3 数据规模决定算法选择
n 最大 10000,50% 是 1000。如果用链表模拟插入,O(n²) 在 10000 时是 10⁸ 级别,勉强能过但很险;更稳的做法是用树状数组或线段树维护“第 k 个未接收位置”,把单次查询降到 O(log n)。不过对小学组来说,O(n²) 的数组模拟(每次线性扫描找第 k 个未收的)在 n = 10000 时大约 5000 万次操作,C++ 下 1 秒内通常能跑完,属于“能过但不优雅”的解法。这道题真正的难点不在复杂度,而在把环形、跳过已收、间隔数递增这三个条件同时正确建模——任何一处理解偏差都会导致样例都过不了。
5. 粉刷匠:逆向思维与百万级数据的降维
5.1 正向模拟为什么不可行
n 行 m 列,k 次操作,每次刷一整行或一整列。n、m、k 最大都是 10⁶。如果真去维护一个二维数组,10⁶ × 10⁶ 直接爆内存;就算只记录操作,正向模拟每次刷一行要改 m 个格子,总操作量 10⁶ × 10⁶ = 10¹²,完全不可能。
这道题的设计意图非常明确:逼你放弃“模拟过程”,转而“看结果由谁决定”。
5.2 逆向扫描的关键洞察
最后一次操作会覆盖它所在行或列的所有格子。倒数第二次操作,如果它刷的行/列和最后一次不同,那么它覆盖的格子里,除了被最后一次交叉覆盖的那个点,其余都保留。以此类推,从后往前扫,每次操作真正“生效”的格子数 = 该行(或列)的总格数 − 已经被后续操作覆盖的交叉点数。
具体做法:记录每一行最后一次被刷的时间戳和颜色,每一列同理。然后从后往前遍历操作,维护“已经被后续操作占用的行数”和“列数”。对于第 i 次操作(刷行 y),它新确定的蓝色格子数 = (m − 已占用的列数)×(z == 1 ? 1 : 0),同时把这一行标记为已占用。列操作对称处理。
// 核心思路示意:从后往前,统计每次操作新影响的格子 // row_used / col_used 记录该行/列是否已被更晚的操作覆盖 // 答案累加时只算“新确定”的格子,颜色由当前操作决定这个思路把 O(nm) 的网格操作降到了 O(k),因为每次操作只做常数次判断。参数上要注意:n 和 m 可以到 10⁶,但 k 也是 10⁶,所以答案可能很大,用long long存结果;行号和列号从 1 开始,数组要开到 10⁶ + 1。
5.3 这道题教的事:先看数据范围再动手
30% 的数据 n, m, k ≤ 1000,暴力模拟能拿 30 分;另外 30% 数据 n ≤ 10 但 m、k 到 10⁵,说明可以枚举行但列要优化;100% 数据全拉满,必须用逆向思维。这种“分层给分”的设计在竞赛里很常见,它的潜台词是:不要一上来就追求最优解,先拿稳部分分,再根据数据范围的特征找突破口。粉刷匠这道题,如果读题时先看数据规模表,就会立刻意识到正向模拟没戏,从而主动往“最后一次操作决定最终颜色”这个方向想——这是血泪经验里最值钱的一条。
6. 易漏点与易错点:四道题里埋的那些坑
6.1 易漏点一:距离单位与房号差混淆
好朋友题里,相邻房子距离 10 米,距离 = 房号差 × 10。很多人写完abs(i - x)就交卷了,忘了乘 10。为什么漏?因为题目描述里“10 米”出现在很靠前的位置,读题时注意力还在“买房”“预算”这些词上,单位信息容易被跳过。怎么补?养成习惯:凡是题目给了物理单位或换算关系,读完立刻在草稿纸上写一行“距离 = 房号差 × 10”,编码时对着抄。
6.2 易漏点二:前缀和减的是 x-1 不是 x
统计人口题里,区间 [x, y] 的和是pre[y] - pre[x-1]。写成pre[y] - pre[x]的话,会把第 x 户漏掉。为什么漏?因为直觉上“从 x 开始”就想减pre[x],但前缀和的定义是“前 i 项之和”,pre[x]包含了第 x 项,要得到从 x 开始的和必须减到 x-1。怎么补?记一句口诀:“前缀和区间,左减一”。或者用 0 下标思维验证:如果 x = 1,pre[0]必须是 0,减pre[0]才对,减pre[1]就把第一户丢了。
6.3 易错点三:卫星题的“间隔”是在未接收里数
卫星题最大的理解陷阱是“间隔 k 颗”到底怎么数。规则说“接收过的卫星不再接收第二次信号,即此卫星不计算在间隔的卫星数量中”,这意味着数间隔时只数还没接收的。很多人按物理位置直接跳,把已收的也算进去,结果样例都过不了。为什么错?因为日常语言里“间隔”是按位置数的,但题目重新定义了计数规则。怎么补?读题时把“不计算在内”这几个字圈出来,然后用 n = 5 的样例手动走一遍,确认自己的理解和样例输出一致再动手写代码。
6.4 易错点四:粉刷匠的时间戳方向搞反
粉刷匠要从后往前扫,因为后面的操作覆盖前面的。如果从前往后扫,每次操作都会被后续操作部分覆盖,没法直接累加。为什么容易反?因为人读操作序列习惯从第一条开始。怎么补?在草稿上把操作编号标出来,画一个箭头从 k 指向 1,提醒自己“最后刷的说了算”。另外注意:同一行被刷多次时,只有最后一次的颜色有效,逆向扫描天然处理了这一点,正向则要额外记录。
6.5 易漏点五:输入输出效率与文件格式
整份卷子有两道题明确提示用 scanf/printf,而且比赛要求按题目英文名建目录、源程序放对应目录下。很多人代码写对了,但文件建错位置或者用了 cin 被卡时限,白白丢分。为什么漏?因为平时练习都在在线评测上点提交,不涉及目录结构。怎么补?赛前用模拟环境练一次:在本机建friend/、people/、star/、paint/四个目录,把源程序分别放进去,确认路径无误。输入输出上,数据量过万就默认用 scanf/printf,别赌评测机性能。
7. 复习清单:从这份卷子倒推的查漏补缺路线
把四道题拆完,复习重点其实已经浮出来了。下面这张表是按“考点—对应题目—自检标准”整理的,可以对着逐项打勾:
| 考点 | 对应题目 | 自检标准 |
|---|---|---|
| 多条件枚举与边界排除 | 好朋友 | 能一次写对三个 continue 的顺序,且距离乘 10 不漏 |
| 前缀和区间查询 | 统计人口 | 能默写pre[y] - pre[x-1]并解释为什么减 x-1 |
| 环形模拟与规则理解 | 卫星 | 能用 n=5 手动复现样例,说清“间隔”的计数范围 |
| 逆向思维与时间戳 | 粉刷匠 | 能说出为什么从后往前扫,并手算样例 2 的答案 |
| 数据规模与复杂度判断 | 全部 | 看到 n、m、k 上限能立刻判断暴力是否可行 |
| 输入输出效率 | 统计人口、粉刷匠 | 数据量过万时默认 scanf/printf,不依赖 cin 优化 |
复习的时候建议按这个顺序走:先限时 150 分钟完整做一遍,模拟真实比赛节奏;然后对答案,把错题按上表归类,看是“理解错题意”还是“算法选错”还是“边界写漏”;最后针对错得最多的那一类,找同类型的题再练三道。最容易忽略的是“读题阶段”的复盘——很多人只复盘代码,不复盘自己第一遍读题时漏了什么信息。我的习惯是每次模拟赛后,把每道题的题面重新读一遍,用红笔标出第一遍没注意到的约束条件,比如“Pi = 0 表示不可卖”“已接收的不计入间隔”“答案不超过 2³¹−1”。这些细节往往就是 10 分和 100 分的差距。
从那以后我每次带人备赛,都强制走一遍“先读数据范围表、再读样例解释、最后读问题描述”的倒序读题法,因为数据范围告诉你算法上限,样例解释告诉你规则细节,问题描述反而最容易带着先入为主的误解去读。这份四道题的卷子,拿去做一次完整的限时自测,再按上面的清单逐项补漏,比刷十套零散的题都管用。希望帮到你。
本文还有配套的精品资源,点击获取