在信息学竞赛的赛场上,有一个残酷的现实:不是每一道题你都能做出来。
即便是最终获得省一的选手,也极少有人能在赛场上AK(全对)。更多的场景是:你盯着屏幕上的题目,思路卡壳,时间一分一秒流逝,而这道题还有几十分甚至上百分的分数在向你招手。这时候,难道只能束手无策、交白卷吗?
当然不是。
在OI赛制(信息学奥赛赛制)中,题目通常设有多个子任务,每个子任务对应特定的数据范围和性质。这意味着,即使你写不出正解,依然可以通过各种技巧获取部分分数。圈内流传着这样一句口诀:“骗分过样例,暴力出奇迹,暴搜挂着机,打表出省一”。这并非鼓励投机取巧,而是在有限的时间内,用最务实的方式争取每一分。
本文将系统梳理信奥赛中常用的骗分技巧,从最简单的“输出特判”到进阶的“暴力枚举”,再到“打表找规律”,帮助你在不会做正解的情况下,依然能够从容拿分。
一、 基础骗分:不动脑子也能拿分
有些骗分技巧几乎不需要思考,只需读懂题目中的关键信息,就能轻松拿到10-30分。这些方法是保底分数的来源。
1. 输出无解情况
很多题目在描述中会明确写道:“若无解,请输出-1”或“如果不存在满足条件的情况,输出NO”。如果你实在不会做,或者时间只剩最后几分钟,直接输出题目要求的无解标识往往能拿到一个测试点的分数。
以CSP-J 2024的一道真题为例,题目要求用班费购买三种物品,若无法花光班费则输出-1。经过分析可知,当n=1、2、5时确实无解。如果你在考场上无法推出正解,直接写一句 if(n==1||n==2||n==5) cout<<-1; else cout<<-1;(当然更严谨的做法是直接输出-1),就能拿到10分。类似地,CSP-S 2023的T3一元二次方程,题面明确要求无解时输出“NO”,直接输出这个结果也能得到10分。
2. 直接输出样例
每道题都会给出一组“样例输入”和“样例输出”。有些比赛的评测机制中,第一个测试点可能就是样例本身。如果你完全不会做,直接把样例输出写进程序,有可能蒙对一个测试点。
虽然这很大程度上依赖运气,但总比空着强——空着是0分,蒙对了就是10分。
3. 输出随机数
对于答案范围较小的题目,输出随机数也是一种“听天由命”的策略。使用 rand() % X 生成一个0到X-1之间的随机整数,如果答案恰好落在这个范围内,就有可能得分。有传言称,NOIP2013有人最后一题输出随机数,竟然得了70分。当然,这属于极端案例,但也说明在某些情况下,随机数确实能撞上正确答案。
二、 暴力枚举:最简单的得分利器
如果说输出特判是“碰运气”,那么暴力枚举就是“凭实力拿分”。所谓暴力枚举,就是用最直接的方式遍历所有可能的情况,虽然效率低,但在数据范围较小时完全可行。
1. 循环遍历
对于序列类问题,当数据范围较小(如n≤100或n≤500)时,直接写几重循环遍历所有可能,往往能通过部分测试点。
以区间最值问题为例,大牛可能会写线段树,但新手完全可以写一个简单的循环:
cpp
for(int i=a; i<=b; i++) {
if(h[i]<min) min=h[i];
if(h[i]>max) max=h[i];
}
2. DFS万能搜索
有一句话说得好:“万物皆可搜”。动态规划不会?DFS。图论不懂?DFS。数学题没思路?还是DFS。
以经典的采药问题为例,M≤100时,直接用DFS枚举每种草药采或不采,就能拿到30%的分数。很多动态规划题,都可以先用DFS写出暴力版本,拿到部分分后再逐步优化。
3. 分支枚举
有些题目情况有限,可以用多个if-else分支逐一处理。比如一元二次方程,虽然题目描述复杂,但数学解法无非就是几种情况:有解且能整除、有解但不能整除、无解。把这些情况用if-else写出来,就能拿到40分左右。
三、 打表:针对小数据的“核武器”
打表是骗分技巧中的“核武器”,用得好可以拿到意想不到的高分。它的核心思想是:在本地预先计算出所有可能的答案,直接以数组形式写入代码。
1. 小范围直接打表
当题目输入范围很小时,完全可以针对每个输入直接输出答案。以NOIP2003的“栈”问题为例,n≤18,答案序列是固定的卡特兰数:1,2,5,14,42,132,429… 你只需要定义一个数组存下这些值,输入n后直接输出 ans[n-1],就能AC(通过)。
2. 特殊性质打表
有些题目虽然整体数据范围大,但部分测试点具有特殊性质。例如CSP-J 2024的“小木棍”题目,前两个测试点n≤20和n≤50,完全可以通过暴力枚举在本地跑出所有答案,然后直接打表提交。
再比如“炸毁计划”一题,经过分析发现,对于大多数询问,答案都是“yes”。直接输出yes可以拿到80%的分数。这就是通过发掘题目规律进行的“智能打表”。
3. 打表找规律
对于数论类题目,可以先写出暴力程序,输出小数据下的结果,观察输入与输出的关系。例如,输出n=1~20对应的答案,可能发现它们满足某种递推关系或数学公式。找到规律后,就可以直接套用公式计算,而不必纠结于正解的复杂推导。
四、 骗分的艺术:如何组合使用多种技巧
真正的赛场上,往往需要综合运用多种骗分技巧,针对不同题目灵活应对。
1. 先看数据范围
拿到试卷后,不要急着做题,先把四道题都浏览一遍,重点关注每道题的数据范围和子任务划分。对于n≤20的题目,DFS或状态压缩可能是可行的;对于n≤5000的题目,O(n²)的算法或许能过;对于n=10^5的题目,就只能考虑部分分了。
2. 分段编程
对于一道题,可以针对不同的数据范围写不同的代码段:
cpp
if(n <= 20) {
// 暴力DFS
} else if(n <= 5000) {
// O(n²)动态规划
} else {
// 实在不会,输出-1或随机数
}
这种“分段暴力”的策略,可以在不确定正解的情况下,尽可能多地覆盖子任务。
3. 实战案例:CSP-S 2020儒略日
这道题当年让无数选手折戟。但如果运用骗分技巧:
4. 实战案例:NOIP 2020排水系统
这道题考察拓扑排序,但数据范围显示结果可能爆long long。如果你注意不到这一点,可能会丢失大量分数。但如果你会“先除后乘”的技巧,就能避免溢出,拿到100分。这其实也是一种“防爆零”的骗分思维——不仅要拿分,还要避免因细节丢分。
五、 骗分的境界:从“瞎搞”到“有策略地拿分”
最高级的骗分,不是胡乱输出一通,而是有策略地分析题目,尽可能多地覆盖子任务。
1. 利用题目提示
很多题目的变量名、函数名本身就包含信息。例如,名为 dp 的数组大概率是动态规划,名为 vis 的数组用于标记访问,名为 lca 的函数是求最近公共祖先。看懂这些命名,有助于快速理解代码意图。
2. 子任务分析法
正规比赛都会在题面中给出子任务说明。例如:
- 对于30%的数据,n≤10
- 对于60%的数据,n≤1000
- 对于100%的数据,n≤10^5
这意味着,即使你写不出满分算法,也可以针对前30%或60%的数据编写专门的代码。在NOIP 2020的“移球游戏”中,即便想不出正解,利用n=2的特殊情况也能拿到10分。
3. 对拍验证
如果你写出了一份“可能正确”的程序,如何验证它是对的?对拍!写一个暴力程序(保证正确但慢)和一个优化程序(你怀疑是正解),用随机数据反复测试,如果输出一致,说明你的优化程序很可能是对的。这种方法虽然不能直接得分,但能帮你避免因算法错误导致的“爆零”。
六、 结语:骗分不是终点,而是起点
行文至此,有必要做一个重要的澄清:骗分技巧是为了在赛场上最大化得分,而不是替代正常的学习。
对于初学者,骗分能让你在不会做正解时依然获得成就感,避免“爆零”带来的打击。对于进阶选手,骗分是检验思路、争取保底分数的必要手段。但无论如何,骗分不能替代扎实的算法学习。
真正的信奥高手,既能在会做的题上稳稳AC,也能在不会做的题上巧妙骗分。他们懂得:赛场上每一分都来之不易,用最聪明的方式拿到能拿的分,才是真正的智慧。
最后,送给大家两句话:
- 会做的题,一分不丢
- 不会做的题,能骗一分是一分
参考文献: