新版骗分导论 - 修订版
Jerrycyx
·
2022-02-08 14:07:27
·
算法·理论
\Huge\text{新版骗分导论}
\small\texttt{THE NEW GUIDE OF CHEATING IN INFORMATICS OLYMPIAD}
\large\text{蒟\ 蒻\ 的\ 宝\ 书}
\normalsize\overline{\qquad}\tiny\normalsize\overline{\qquad}
\texttt{\small 修订版 - 第 11.6 版} \\ \texttt{ \scriptsize Last Update on 2025.8.29}
修订——Jerrycyx(洛谷, 博客园, CSDN)
本文在以下平台同步发送:洛谷, 博客园, CSDN
前言
(为节省时间,前言可以只看加粗部分)
《骗分导论》在网络上流传甚多,但是质量却大多参差不齐,因为无脑的直接搬运复制,某些版本(包括根本不排版的原文)的可读性就更加令人堪忧,对于广大蒟蒻十分不友好。
蒟蒻们需要一份可读性强、美观、实用的《骗分导论》,如此才称得上真正的“蒟蒻的宝书”,于是,本文诞生了。
本文旨在成为网络上观感、可读性、具体性最好的《骗分导论》。 为此,在经过多轮更新迭代后,本文在保留原文内容的情况下,做出了以下努力:
参照《洛谷主题库题解规范(2023 试行版)》和《洛谷主题库题目规范》,对原文进行了全面的重新排版,包括但不限于:
为全文的标题行添加各个层级的标题行格式
将所有的代码内容添加代码块格式(包括行内代码块和行间代码块)
为原文添加了 \KaTeX 格式,具体如下:
数学公式(运算式、运算符、参与运算的常数、作为变量的字母等)应正确使用 LaTeX,非数学公式(一般英文单词、题目名、算法名、人名等)不应使用 LaTeX。
——《洛谷主题库题解规范(2023 试行版)》 / 《洛谷主题库题目规范》
为原文添加了应有的空格,具体如下:
中文与英文、数字或公式之间以半角空格隔开,但中文标点符号与英文、数字或公式之间不应有空格。
——《洛谷主题库题解规范(2023 试行版)》 / 《洛谷主题库题目规范》
更正了原文中不正确的全角/半角标点符号(中文/英文标点符号)使用,例如代码中的分号
美化了目录格式,增强可读性的同时,使用引用格式以避免博客/专栏系统的误检测
将原文中所有的直接题目内容替换为洛谷上相应的题目链接
特别地,洛谷上及网络上都没有的题,均以用户题目的形式(格式已参考《洛谷主题库题目规范》进行排版)上传到洛谷并将题目内容替换为题目链接(甚至有一道题造了测试数据,并配合原文使蒟蒻们能骗到相应的分数)
为原文添加了注释以便蒟蒻们更好地理解文章
对原文内容进行了些许补充(均在文中注明以与原文区分开)
还有很多细节上的优化,这里就不一一列出了
这些优化的对文章的提升是巨大的(读者可自行比对本文和原文),但由于本人水平限制,本文难免会有错漏之处。如有不足,请通过博客/专栏评论或洛谷私信的方式联系并提出修改建议,谢谢!
如果认为本文不错的话,也可点赞、收藏或分享给别人一起体验骗分的快乐。
初版前言
这是我在倒腾洛谷水贴时偶然翻到的一篇转载博客,自认为很适合 OIer 们,于是便将其重新排版并添加注释后放在这里,以增强其可读性和具体性。
文章中的例题都去洛谷上查过了。若是洛谷题库里有,则将原题目内容替换为题目链接;而洛谷题库没有的,就将其保留(第 9 版已替换为用户题目链接)。
第 11(.6) 版前言 / 更新内容
本次更新主要包括以下内容:
在 3.1 模拟 新增了一道例题(已标明以与原文区分)
更正了两处错误格式
增加了一处附录
略微优化了标题格式
文章末尾补充了一处缺失的原文内容
补充了一处注释(感谢 @LionBlaze 的补充)
更正了多处题目链接文本缺失的题号
另外,感谢@MStar、@林志艺、@LincW、@GoodLuckCat、@LionBlaze 等对该文章提出的建议。
原文链接
原作者:大神cyd(Vijos、洛谷)
修订:Jerrycyx(洛谷, 博客园, CSDN)
目录
第1章 绪论
第2章 从无解出发
2.1 无解情况
2.2 样例——白送的分数
第3章 “艰苦朴素永不忘”
3.1 模拟
3.2 万能钥匙——DFS
第4章 骗分的关键——猜想
4.1 听天由命
4.2 猜测答案
4.3 寻找规律
4.4 小数据杀手——打表
第5章 做贪心的人
5.1 贪心的算法
5.2 贪心地得分
第6章 C++的福利
6.1 快速排序
6.2 “如意金箍棒”
第7章 “宁为玉碎,不为瓦全”
第8章 实战演练
第9章 结语
第1章 绪论
在 OIer 中,有一句话广为流传: 任何蒟蒻必须经过大量的刷题练习才能成为大牛乃至于神牛。 这就是著名的 lzn 定理。然而,我们这些蒟蒻们,没有经过那么多历练,却要和大牛们同场竞技,我们该怎么以弱胜强呢?答案就是: 骗分。
那么,骗分是什么呢?骗分就是用简单的程序(比标准算法简单很多,保证蒟蒻能轻松搞定的程序),尽可能多得骗取分数。 让我们走进这本 《新版骗分导论》,来学习骗分的技巧,挑战神牛吧!
第2章 从无解出发
2.1 无解情况
在很多题目中都有这句话:“若无解,请输出 -1.” 看到这句话时,骗分的蒟蒻们就欣喜若狂,因为——数据中必定会有无解的 情况!那么,只要打出下面这个程序: printf("-1"); 就能得到 10 分,甚至 20 分,30 分!
举个例子: P1078 [NOIP2012 普及组] 文化之旅
这道题看起来很复杂,但其中有振奋人心的一句话“输出 -1”,我考试时就高兴坏了 (当时我才初一,水平太烂) ,随手打了个 printf("-1");,得 10 分。
2.2 样例——白送的分数
每道题目的后面,都有一组 “样例输入” 和 “样例输出” 。它们的价值极大,不仅能初步帮你检验程序的对错(特别坑的样例除外),而且,如果你不会做这道题(这种情况蒟蒻们已经司空见惯了),你就可以直接输出样例! 例如美国的 USACO,它的题目有一个规则,就是第一组数据必须是样例。那么,只要你输出所有的样例,你就能得到 100 分(满分 1000 分)!这是相当可观的分数了。
现在,你已经掌握了最基础的骗分技巧。只要你会基本的输入输出语句,你就能实现这些骗分方法。那么,如果你有一定的基础,请看下一章——我将教你怎样用简单方法骗取部分分数。
第3章 “艰苦朴素永不忘”
本章的标题来源于《学习雷锋好榜样》的一句歌词,但我不是想教导你们学习雷锋精神,而是学习骗分!
看到“朴素”两个字了吗?它们代表了一类算法 [注1:朴素算法,英文术语为 brute-force,也可直译为“暴力求解”,主要有模拟和 DFS 等] 。下面我就来介绍它们在骗分中的应用。
3.1 模拟
所谓模拟,就是用计算机程序来模拟实际的事件。例如 P1076 [NOIP2012 普及组] 寻宝,就是写一个程序来模拟小明上藏宝塔的动作。
较繁的模拟就不叫骗分了,我这里也不讨论这个问题。
模拟主要可以应用在骗高级数据结构题上的分,例如线段树。下面举一个例子来说明一下。
P2880 [USACO07JAN] Balanced Lineup G
对于这个例子,大牛们可以写个线段树,而我们蒟蒻,就模拟吧。
附模拟程序:
for(int i=1;i<=q;i++)
{
scanf(“%d%d”,&a,&b);
int min=INT_MAX,max=INT_MIN;
for(int i=a;i<=b;i++)
{
if(h[i] if(h[i]>max)max=h[i]; } printf(“%d\n”,max-min); } 程序简洁明了,并且能高效骗分。本程序得 50 分。 修订增:用这种方法你甚至可以 AC 蓝题。 P2571 [SCOI2010] 传送带,本题正解为三分套三分,但是因为答案要求精度较低,所以可以暴力切掉。 将这道题中两条线段分别平均拆成 S 个点,枚举离开和进入传送带的位置(S 最大可以取到 5000+,但是因为数据较水,最低甚至取 52 就能 AC),将两条线段上的这些点暴力配对进行计算时间,最后取最小值即为答案。 可以参考这篇题解 3.2 万能钥匙——DFS [注2:即“深度优先搜索”算法。] DFS 是图论中的重要算法,但我们看来,图论神马的都是浮云,关键就是如何骗分。下面引出本书的第 2 条定理:DFS 是万能的。 这对于你的骗分是至关重要的。比如说,一些动态规划题,可以 DFS;数学题,可以 DFS;剪枝的题,更能 DFS。下面以一道省选题为例,解释一下 DFS 骗分。 例题: P1048 [NOIP2005 普及组] 采药 这题的方法很简单。我们瞄准 20% 的数据来做,可以用 DFS 枚举方案,然后模拟计算出最优解。 附一个大致的代码: void DFS(int d,int c){ if(d==n){ if(c>ans)ans=c; return; } DFS(d+1,c+w[i]); DFS(d+1,c); } 第4章 骗分的关键——猜想 4.1 听天由命 如果你觉得你的人品很好,可以试试这一招——输出随机数。 先看一下代码: #include #include #include // using namespace std; int random(int start,int end) //随机数函数 { end++; int res=start+(end-start)*rand()/(RAND_MAX + 1.0); return res; } int main() { srand(unsigned(time(0))); //利用时间确定随机数种子 for(int i=1;i<=1000;i++) printf("%d\n",random(1,100)); //重复一千次输出 1~100 之间的任意数 return 0; } 有关随机数的具体叙述,可以自行百度 [注3:可参考资料] 这种方法适用于输出一个整数(或判断是否)的题目中,答案的范围越小越好。让老天决定你的得分吧。 据说,在 NOIP2013 中,有人最后一题不会,愤然打了个随机数,结果得了 70 分啊!! 4.2 猜测答案 有些时候,问题的答案可能很有特点:对于大多数情况,答案是一样的。这时,骗分就该出手了。你需要做的,就是发掘出这个答案,然后直接输出。 有时,你需要运用第 3 章中学到的知识,先写出朴素算法,然后造一些数据,可能就会发现规律。 例如,本班月赛中有一道题: U480179 【新版骗分导论 - 修订版】炸毁计划 你发现问题了吗?那么多座桥,炸一座就破坏岛屿的联系,可能性微乎其微(除非特别设计数据)。那么,我们的骗分策略就出来了:对于所有询问,输出 yes。果然,此算法效果不错,得 80 分。 现在知道猜测答案的厉害了吧? 修订增:P8819 [CSP-S 2022] 星战:只要读完题就可以发现,这道题 NO 的概率特别高,所以可以尝试对于每一个问题都输出 NO,可得 45 分,对于 T3 已经是很高的得分了。 但是相应的,2022 年的 CSP-S 分数线暴涨,比往年多了好几十分。 OIer 们对此表示了强烈不满,详见:不可以,总司令 4.3 寻找规律 首先声明:本节讲的规律不是正当的算法规律,而是数据的特点。 某些题目会给你很多样例,你就可以观察他们的特点了。有时,数据中的某一个(或几个)数,能通过简单的关系直接算出答案。 只要你找到了规律,在很多情况下你都能得到可观的分数。 这样的题目大多出现在 NOI 或更高等级的比赛中,本人蒟蒻一个 [注4:别信,大神cyd 很强的!] ,就不举例了。传说某人去省选时专门琢磨数据的规律,结果有一题得了 30 分。 4.4 小数据杀手——打表 我认识一个人,他在某老师家上 C 语言家教,老师每讲一题,他都喊一句:“打表行吗?” 他真的是打表的忠实粉丝。表虽然不能乱打,但还是很有用的。 先看一个例子:P1044 [NOIP 2003 普及组] 栈 这题看似复杂,但数据范围太小,N \le 18。所以,骗分程序就好写了: int a[18]={1,2,5,14,42,132,429,1430,4862,16796,58786,208012,742900,2674440,9694845,35357670,129644790,477638700}; scanf("%d",&n); printf("%d",ans[n-1]); 测试结果不言而喻,AC 了。 学完这一章,你已基本掌握了骗分技巧。下面的内容涉及一点算法知识,难度有所增加。蒟蒻中的蒟蒻可以止步于此了。 第5章 做贪心的人 5.1 贪心的算法 给你一堆纸币,让你挑一张,相信你一定会挑面值最大的。其实,这就是贪心算法。 贪心算法是个复杂的问题,但你不用管那么多。我们只关心骗分。给你一个问题,让你从一些东西中选出一些,你就可以使用贪心的方法,尽量挑好的。 举个例子:这是我们的市队选拔的一道题。 U480191 【新版骗分导论 - 修订版】有趣的问题 这题让人望而生畏,但我们有贪心的手段。每个二元组的 a 值是乘到答案中的,所以 a 越大越好,那么只要选择出最小的 k 个去掉即可。代码就不写了,因为这个涉及到下一章的内容:排序。 此代码得 20 分。 5.2 贪心地得分 我们已经学了很多骗分方法,但他们中的大多效率并不高,一般能骗 10 \sim 20 分。这不能满足我们的贪心。 然而,我们可以合成骗分的程序。举个最简单的例子,有些含有无解情况的题目, 它们同样有样例。我们可以写这个程序 if(是样例) printf(样例); else printf(“-1”); 这样也许能变 10 分为 20 分,甚至更多。 当然,合并骗分方法时要注意,不要重复骗同一种情况,或漏考虑一些情况。 大量能骗分的问题都能用此法,大家可以试试用新方法骗 2.1 中的例子“P1078 [NOIP2012 普及组] 文化之旅 ”。 第6章 C++ 的福利 (请 P 党们 [注5:指 Pascal 语言的使用者] 跳过本章,这不是你们的福利) 在 C++ 中,有一个好东西,名唤 STL ,被万千 OIer 们所崇拜,所喜爱。下面让我们走进 STL。 6.1 快速排序 快速排序是一个经典算法,也是 C++ 党的经典福利。他们有这样的代码: #include sort(A,A+n);//对一个下标从0开始存储,长度为n的数组升序排序 就这么简单,完成了 P 党一大堆代码干的事情。 [注6:Python 中也有类似的排序函数] 6.2 “如意金箍棒” C++ 里有一种东西,叫 vector 容器。它好比如意金箍棒,可以随着元素的数量而改变大小。它其实就是数组,却比数组强得多。 下面看看它的几种操作: vector V.push_back(x);//末尾增加一个元素x V.pop_back();//末尾删除一个元素 V.size();//返回容器中的元素个数 //它同样可以使用下标(如 V[n] )访问。(从0开始) [注7:vector 常数较大,即使开了 O2 以后速度也不到数组的一半,还请读者斟酌使用;同时提供参考资料] 第7章 “宁为玉碎,不为瓦全” 至此,我已介绍完了我所知的骗分方法。如果上面的方法都不奏效,我也无能为力。但是,我还有最后一招—— 有句古话说:“宁为玉碎,不为瓦全”。我们蒟蒻也应有这样的精神。骗不到分,就报复一下,卡评测以泄愤吧! 卡评测主要有两种方法: 利用死循环,故意超时; 进入终端,卡住编译器。 先介绍下第一种。代码很简单,请看: while(1); 就是这短短一句话,就能卡住评测机长达 10s,20s,甚至更多!对于测试点多、时限长的题目,这是个不错的方法。 第二种方法也很简单,但危害性较大,建议不要在重要比赛中使用,否则可能让你追悔莫及。它就是: #include #include //Linux系统中使用 [注8:这里只用在代码中加入以上两个头文件即可,不过实际使用时可能会编译错误(至少在 Windows 操作系统上使用 Dev-C++ 5.11 和编译器 TDM-GCC 4.9.2 Release 没有成功编译)] [注8.1:con 和 /dev/console 是指命令行,在本地编译会一直去读命令行,但是你没有输入任何东西。 实际上,在一个好的评测系统上,去读这两个文件,要么直接读到 EOF 相当于啥也不干,要么直接 Permission Denied 导致 CE。——来自 @LionBlaze] 它非常强大,可以卡住评测系统,使其永远停止不了编译你的程序 [注9:参考资料] 。唯一的解除方法是,工作人员强行关机,重启,重测。当然,我不保证他们不会气愤地把你的成绩变成 0 分。请慎用此方法。 第8章 实战演练 下面我们来做一些习题,练习骗分技巧。 我们来一起分析一下 NOIP2013 普及组的试题吧。 P1980 [NOIP2013 普及组] 计数问题 P1981 [NOIP2013 普及组] 表达式求值 P1982 [NOIP 2013 普及组] 小朋友的数字 P1983 [NOIP 2013 普及组] 车站分级 第 1 题,太弱了,不用骗,得 100 分。 第 2 题,数据很大,但是可以直接输入一个数,输出它 \mod 10000 的值。得 10 分。 第 3 题,是一道非常基础的 DP,但对于不知 DP 为何物的蒟蒻来说,就使用暴力算法(即 DFS)。得 20 分。 第 4 题,我们可以寻找一下数据的规律,你会发现,在所有样例中, M 值即为答案。所以直接输出 M ,得 10 分。 这样下来,一共得 140 分,比一等分数线还高 20 分![注10:指部分地区的一等分数线] 你的信心一定会得到鼓舞的。这就是骗分的神奇。 第9章 结语 骗分是蒟蒻的有力武器,可以在比赛中骗得大量分数。相信大家在这本书中收获了很多,希望本书能帮助你多得一些分。但是,最后我还是要说一句: \Huge \bf \text{不骗分,是骗分的最高境界。} 作者:大神cyd QQ:724612372 修订:Jerrycyx 修订附:进阶与拓展 如果有时间并想要更进一步的话推荐阅读李博杰的《骗分导论》,原文很长(两百多页的 PDF),但所述十分全面。 它其实更像是在总结 OI 中的经验与技巧,而非单纯的骗分。而这些恰恰是寻常算法竞赛书籍所不讲的,也正是新手们所欠缺的东西。 同时,即使对于 NOI 级别的选手,其也有很高的参考价值,有助于拓展知识面,增强得分能力。 官方 PDF:点击阅读 官方发布地址:骗分导论 | Bojie Li 本文采用 CC-BY-NC 4.0 创作共享协议,转载请注明作者及出处,禁止商业使用。