一、什么是大模拟
大模拟题的特点是:规则繁琐、边界密集、没有算法捷径,按题意一步步实现即可,但代码长、细节多、极易写挂。历年 CSP-S 的 T1/T3 常客:2020 儒略日(日期换算)、2023 结构体(类型系统)、2019 格雷码等。
生活类比:大模拟就像照着一份 20 页的宜家说明书组装家具。不需要你懂力学原理,但漏装一个垫片、装反一块板,最后柜子就是歪的。得分高低取决于流程和细心,不取决于聪明。
考场定位:大模拟题往往是"区分度守门员"——大家都会做,但满分率低。策略是预留充足时间 + 结构化写法 + 小样例逐行走查,而不是拼手速。
二、标准解题流程
- 读题列要素:把规则拆成一张清单:有哪些对象、对象有哪些属性、每条规则触发什么变化。写在草稿纸上,不漏一条。
- 设计数据结构:每个对象一个 struct,属性与清单一一对应;能用数组就不用 map。
- 拆函数:每条独立规则一个函数(如
move()、leapYear()),主流程只负责按顺序调用。函数越短,错得越少。 - 小样例手算:写完先用题目的样例在纸上走一遍代码,再跑程序对照。大模拟的调试成本远高于预防成本。
- 造边界数据:n=1、最大 n、全相同、环形跨边界、闰年 2 月……逐个验证。
三、高频技巧:环形结构用同余
环形排列(围成一圈的玩具、循环队列、星期推算)中,位置变化统一写成:
新位置 = (pos + step % n + n) % n
+ n 是为了吃掉负数(逆时针走时 step 为负),保证结果落在 [0, n)。一切"转圈"问题都不要写 if 判断越界,用同余一行解决。
实例:P1563 玩具谜题的方向规律
玩具有朝向 face(0 朝内 / 1 朝外),指令有方向 dir(0 左 / 1 右)。把四种组合画在圆圈上验证,可得唯一需要记的规律:
face == dir → 沿编号减小方向走;face ≠ dir → 沿编号增大方向走
| 朝向 face | 指令 dir | face 与 dir 关系 | 下标变化 |
|---|---|---|---|
| 0(朝内) | 0(左数) | 相同 | 减 s |
| 0(朝内) | 1(右数) | 不同 | 加 s |
| 1(朝外) | 0(左数) | 不同 | 加 s |
| 1(朝外) | 1(右数) | 相同 | 减 s |
这题的唯一难点就是方向。务必自己画一个 4 个位置的圆圈,把 (face, dir) = (0,0)、(0,1)、(1,0)、(1,1) 四种情况各走一格验证——考场上靠背诵不如靠画图。
四、实例:日期类大模拟(2020 儒略日 P7075)要点
- 核心思路:日期 ↔ 天数的双向换算。先把"公元前 4713 年 1 月 1 日"作为第 0 天,输入的天数 r 直接加上偏移,再反推年月日——永远不要在"月份"层面做加减。
- 闰年规则:儒略历每 4 年一闰;格里历"百年不闰、四百年再闰"。两套规则写成两个函数,分界日 1582-10-04 / 1582-10-15 特判。
- 年份估计:从天数估年用
y ≈ r / 365 − 微调,再 while 修正,避免从第 0 年逐个累加(会 TLE)。 - 公元前:没有公元 0 年!公元前 1 年的下一年是公元 1 年,输出格式
1 BC的转换要单独写。
五、易错点清单
- 规则"翻译"漏条:写完对着题面逐句打钩,尤其注意"如果……则……否则"的否则分支。
- 多组数据:每组结束后数组、计数器、标记全部重置(最好封装
init())。 - 下标从 0 还是从 1、区间左闭右闭还是左闭右开——在代码开头写注释固定下来。
- 取模的负数陷阱:
(a % n + n) % n。 - 输出格式:空格、换行、前导零、大小写(
BC前后空格)逐个对样例。
六、练习
- P1563 玩具谜题(环形同余入门)
- P7075 儒略日(日期换算综合)
- P9754 结构体(CSP-S 2023 T3,类型系统模拟)
- P1514 引水入城(BFS 可达性 + 区间覆盖贪心的复合)