出题!出题!出题!

存题网站
想要出一个很好的题,当然需要一个很好的存题的地方,
我们机房比较常用的存题地方有:洛谷个人题库、Hydro 个人域、写好题面之后本地存储。本地存储将会在下一节中介绍,现在我们先介绍几个好的存题网站:洛谷、Hydro、Polygon。三者的优势大概如下:
- 洛谷:用的最多,配置也不复杂,方便。
- Hydro:支持多元化的题目类型和限制。方便各种类型的题。
- Polygon:支持系统化的出题流程和造数据。方便出题和合作出题。
洛谷
洛谷是最方便的存题网站地了。直接在“我的题目”里新建,写好题面、std,数据打好包往上一传,可视化地配置 subtask 和时空限制,整个过程如行云流水,一目了然。
大家接触洛谷接触得最多,所以不需要详细讲啦!
主要优点:
- 方便。
- 网站大众,出好题后便于给他人验。
主要缺点:
- 有严格的数据点大小、数量和时空限制。
- 通信题、客观题(类似于 NOI 笔试)无法实现。
Hydro
Hydro 也是一个放题的不错的选择,建立一个域,把出题组的人往里面一拉,新建题目,和洛谷类似地方便。
Hydro 的配题也更为灵活,因为甚至连评测流程、评测器等都是可以自定义的。(例如,出一个交互题,要求两次询问之间不能用全局变量传递答案,就需要在每次询问前,结束当前代码运行,重新运行选手代码,类似这样的自定义评测流程在洛谷是无法做到的。)你也可以对于每个题单独限制提交代码,避免 python 草高精的尴尬场面。再比如,评测中打开选手的源代码,在 checker 里写个 kmp 匹配 int128
,如果发现选手用到了 int128
直接判错,等等。
主要优点:
- 所有题型都支持:传统题、提交答案、通信题、交互题、客观题、Quine(写一个代码让其输出自己的源代码)、甚至白盒测试。
- 功能强大,几乎可以卡掉所有你想卡掉的做法(只需要通过自定义评测流程就能实现大部分你想要的卡法,例如禁止
int128
、bitset
、pbds
、限制提交语言……)。
主要缺点
- 相对麻烦。
- 形式上对数据大小没有限制,但是超过 100MB 的数据上传的等待时间也够你从高一等到大学毕业。
Polygon
Codeforces 唯一指定的出题网站,是一个多人协作的出题平台。虽然不支持那么多的题型,但是功能配置十分强大。在平时出题,尤其是多人合作出题时,Polygon 是极好的选择。
当然你在 Polygon 上出的题,也方便为日后出 CF 做准备(如果有意图的话),存的时候题面可以拿中文存,投的时候改为英文就行了(一般 CF 给你发配的审核员会帮你把英文题面翻译成俄语,比赛时俄语的赛时提问你也可以找审核员帮你解决,因此俄语并不是出 CF 的必学语言)。
Polygon 相比于其他的国内主流网站,它的优点更多地在于出题人出题的体验,而不是题目类型的多样性和毒瘤的多样性。
例如 Polygon 出题有如下特点:
- 版本可持久化,你可以回退到历史版本。多人出题时不会乱作一团,不需要互相传文件。
- 出题系统完善,题面、validator、generator、checker、solution 环环相扣,方便管理出题进度或多人分工。
- 可以为 solutions 设置标签(AC 代码、WA 代码、AC or TLE 代码、AC or WA 代码……)如果代码运行结果和期望结果不一致会警报,方便你逐一卡掉错解。
- 支持站内对拍,拍到的数据可以直接(可以设置为自动)添加到数题目据中。手动上传数据时,只需要上传输入数据,输出数据会根据你的 std 自动生成。
- 设置数据范围后,自动分析数据是否有一定的强度。(例如,若在 validator 中写到 的范围是 ,且你的数据中没有 的点,系统会提示你没有数据把 开到下界,建议补充。)
主要优点
上面这些还不够吗?
主要缺点
外网,有时不挂梯子都跑得贼快有时必须挂梯子。
LaTeX 本地组题
LaTeX 安装
如果要本地存题,我们一般会把题面的 markdown 或者 latex 源代码存着,然后编译出 pdf 格式。
markdown?大家都会对吧,你用个 vscode 或者 typora 随便编译导出一下,然后就没有然后了。这里我们主要来一个 latex 入门。
我用的是 vscode 编辑 tex 文档。首先在 vscode 里安装好一个插件『LaTeX Workshop』,然后安装 texlive:
1 | sudo apt-get install texlive-full cjk-latex latex-cjk-chinese |
配置好你的 vscode 插件(例如我比较习惯编译完成之后自动清空除源文件和 pdf 文件之外的其他所有文件),然后就可以用了。
考虑到只有 XeLaTeX 是能比较好地支持中文的,所以最好选用 XeLaTeX 进行编译,而不是 pdfLaTeX 等等。
LaTeX 入门
头文件、预处理
首先来看几个基本的配置:
1 | \documentclass{article} |
documentclass
是每一个 tex 文件开头必须有的东西,它声明了你的文章的类型。对于出题而言,文本类型为 article
,当然你肯定会碰到很多其他类型,最经典的,比如 beamer
(用 LaTeX 写 ppt 的那个)。
下面的一堆 usepackage
相当于一堆 #include
,声明了头文件。至于两个头文件是分成两个 usepackage
,还是合并成一个用逗号隔开,是随意的。我这么写一般是个人习惯(美观)。
部分头文件可以声明类型,例如 ctex
(这个头文件的作用是让你的 LaTeX 文档支持中文)前面的 UTF-8 表明了这个文档的文本格式是 UTF-8。如果你导出的文本格式是一些别的东西(例如 UTF-16),但是用 UTF-8 的文本格式打开,你就会收获一车乱码(某音游公司老喜欢用这种方法发加密文本了)。
geomentry
控制了你的 pdf 的版面:A4 纸大小、文档内容和上下左右边界的间距。
setmonofont
也就是字面意思,设置你的字体。
为了让后面的编写更为方便,类似于 C++ 中的 #define
,我们需要一个宏定义,方便我们快速修改后面反复出现的内容。例如把这一套题的每个问题的题目名都宏定义了,出另外一套题的时候就不需要在各个显示题目名的地方都改掉了。这样的宏定义在 LaTeX 中为 \def
:
1 | \def\tit{一个普通的互测赛} |
\def
的作用是,把后面花括号里面的内容用一个斜杠开头的东西代替。例如上面的例子里“遥远的念想”一词就被 \Bzh
代替了(注意是 \Bzh
而不是 Bzh
)。
在 tex 文档中,用百分号 %
进行注释。百分号及本行后面的内容都相当于注释。
为了方便把一些信息放在页眉页脚处,我们用 fancy
页面格式(页眉页脚都能自定义),并仿照 CCF 的风格,把页脚设置为页码:
1 | \pagestyle{fancy} |
注意:\pagestyle
用于设置全局页面格式,如果只需要单独一页用上这个东西,请把 \pagestyle
改为 \thispagestyle
。
搞定一些前置之后,我们就可以开始正文了。
正文的内容要从一个 \begin{document}
开始,用 \end{document}
结束:
1 | % 这前面都是些预处理、预定义,里面的内容不会显示在文档里。 |
当然,还要设置好行距,\begin{spacing}
用于控制行距。注意要在 document 里面写。
正文
一套比赛的第一版,按照 CCF 和大部分其他竞赛的题面风格,要放上每道题目的基本信息,还有选手必读的注意事项。
那么我们先放上比赛的标题,和一些其他的乱七八糟的东西(比如时间啊,之类的):
1 | \begin{center} |
\begin{center}
和 \end{center}
:让中间的内容居中。
~\\
相当于换行。
\Huge
、\huge
、\Large
、\large
等等,用于设置字体大小,除了这些,还有 \small
、\footnotesize
、\scriptsize
、\tiny
。
\textbf
用于给里面的内容加粗。
解决完标题行的问题,就应该把每个题的基本题目信息放上去了。众所周知这使用表格实现的。那么如何在 LaTeX 中插入一个表格?(请时刻注意你写的是 tex 不是 md,markdown 语法是没用的。)我们用 \begin{tabular}
和 \end{tabular}
实现,和 \begin{document}
类似,这两个东西中间包括的东西,就是你的表格啦。
1 | \large |
\begin{tabular}
后面可以用大括号扩上一些表格的设置,其中 p{0.2\textwidth}
表示一个单元格的宽度为 0.2 单位长,而两个这个东西之间用一个 |
隔开,相当于这两个单元格之间有竖线分开(不然的话,两个单元格之间是没有竖线分隔的)。如果大括号前面是 p
表示左对齐,如果是 m
则表示居中。如果对单元格宽度没有要求(自动),则将 p
改为 c
,然后去掉大括号内的东西。
\hline
表示一条横线。如果没有的话,当前这一行和下一行之间就没有横线隔开。
\texttt
其实是表示这里面的内容用某种字体样式表示出来。可以设置字体样式的还例如:\textit
、\textsl
、\textsc
、\textrm
。
然后就可以用 &
来表示两个单元格,用 \\
表示换行了。
类似地仿照 CCF 把一些诸如编译命令之类的也加个表格显示就可以了。
1 | 提交源程序文件名 |
然后就是把选手注意事项给丢出来:
1 | \large{\textbf{注意事项(请仔细阅读)}} |
\indent
和 \par
,我也忘记干什么了,反正这里要加上就对了。
第一版的预览如下:
你可以通过这个图与前面的 tex 文档对比,方便更深刻地理解每个地方对应的用处。
然后我们来看一下一个真正的题目应该怎么写,首先用 \newpage
建立一个新页面。还是先把前置细节搞定。注意到第一版是不需要什么页眉的,但是这里开始就应该有个页眉了。
1 | \newpage |
\renewcommand{\headrulewidth}{0.5pt}
表示将页眉的横线粗细设置为 0.5 单位长。如果不想要页眉下面的横线,可以把粗细设置为 0。
我们已经知道了 \begin{center}
可以使后面的东西居中。实际上,如果你要居中的东西不多,可以直接用 \centerline{...}
实现。
注意到写题面总还是要在题面里面写个样例的。LaTeX 没有 markdown 那么简便的代码框,如何放一个样例并有一定的美观程度?
我们用 \begin{lstlisting}
和 \end{lstlisting}
实现。在使用这两个东西之前,需要先在预定义里面写好一个样式,我使用的样式是这样的:
1 | \lstset{ |
然后你就可以用 lstlisting
快乐设计样例了。
我们给出一个题面的例子:
1 | \centerline{\LARGE{\textbf{\Azh(\Aen)}}} |
\emph
也是让后面的字呈现一种特殊的字体。
\Xhline
可以控制表格中这一行的横线的粗细。
理论上,上面这一段文档,你应该已经可以自己脑补出渲染结果了。如果你不能也没关系,我放一下这一版的预览效果:
用类似的方法实现剩下的题目即可。
出题!
出题前置
在出一个题之前,你应该仔细地想清楚:你要出的题目的定位。
一个题可以有很多定位,例如能良好区分参赛选手和混子人的题目、防 AK 的题目、签到题,等等……
如果你需要组一套题目,那么就更应该注意各个定位的题目是否要有。不过,对于一个非严谨的或非官方正式的比赛(例如校内互测赛),可以没有签到题,但一定不能都是防 AK 题。另外,如果是针对一类特定群体的题目(例如校内同届互测赛、省选选拔等等,选手的水平没有极其巨大的跨度),题目既不能过易(不然大家都 AK 了没事干),也不能过难(不然大家都坐牢了没事干)。
而且出一个题就有可能耗费掉你十分多的时间,特别是限定了题目难度定位和题目算法的情况下,出一个符合要求的题目是比较困难的。
另外,还需要慎重考虑你的题目质量。如果是一些板子套板子,或者强行堆码量的题目,一般都不是什么好题。你的题目是要给别人做的,出题不能觉得自己堆一个大模拟非常爽就不考虑考场上做这个题的人的想法。
出一道题首先也得有个 idea,idea 可以有非常多的来源:你做过的其他题目(但是最好不要生搬硬套)、游戏(但是最好不要把游戏写成大模拟),等等。
一般你的出题,最好不要把几个题生硬地拼起来(判断依据:前一部分和后一部分是否完全割裂;例如前一部分解决一棵树上一个点的点权,后一部分是 LCT 模板。即时点权是随意输入的,后半部分也能做;前后没有任何关系。)、或者出一个纯数学(或其它科目)题(注意,OI 题的数学部分一般着重于如何优化数学式子计算,而不是用一堆定理得到答案是什么)。
你的题面最好不要包含一些和 OI 关系不大,又难以理解/超纲的东西(例如大学物理的浮力方程,等等)。
出题
搞定你的 idea 之后,一般要想好你的题目的 solution(当然,有的人出题习惯先想一个很妙的 solution,再把这个模型套在一个事例里出成题目,这也是没有问题的。),如果你发现你的题目 solution 只会无脑暴力,这个题就应该要被毙掉了。
如果你实在不清楚你的题是否有高妙的 solution,可能还需要额外查证一下你这个题是否是 NPC 的,不然就会重演我们学长觉得一个题一定有很妙的解法但是想了一年最后发现这是 NPC 的尴尬历史。
solution 出来后,先别急着写 std,先查重。到各大搜索引擎或题目网站上搜一下你的题目大意和 solution 大意还有基本算法(例如:李超线段树+DP)。看看有没有重题。如果查重发现你的题目是个重题,这个题就应该要被毙掉了。不过对于一些不算很严谨的比赛(例如校内互测),如果这个题没什么人知道,你可以考虑以“搬题人”的身份把这个题“搬”过来。
搞定上面的东西后再写 std。写完 std 后,要和暴力对拍,不然万一你 std 写挂了,这就是一个错题。
最好还要看看有没有别的做法能通过本题,即使是一些很离谱的做法,如果想到了最好也试一试。不然就会重演我们这一届一个人出的 DP 被另外一个人的暴力记忆搜(状态数都超过了 的那种)+减枝干过去还跑得比 std 快的历史。
写题面的时候,要保证你的题面清晰易懂。最好还要给你的小样例加上样例解释。这也同时提示了你最好要搞一个有一定强度的小样例(例如上文『纯粹的记忆』这个题的样例就很不合适)。
输入输出格式应当完整。例如,如果数据范围只写明了 ,则一定要在输入格式或其它地方点明 的下界(例如非负整数、正整数等)。且每个数字是整数还是浮点数,一定要说明清楚。
对于浮点数输出的题,最好不要通过“保留若干位小数输出”的形式输出,这样的话即使一点很小的精度误差都可以让答案不正确(例如标准答案为 ,保留两位小数变成 ,即使你的答案只是因为一个极小的精度误差得到 ,保留两位小数也变成了 ,然后 WA 掉。)。最好的做法是写个 spj,若相对误差或绝对误差不超过一个数,就判为 AC。这是合理的。
对于为了避免输入过大,在选手代码中由出题人编写的数据生成器生成输入的方式,还需谨慎考虑。因为这种方式可能引入了数据的随机性。使得一些基于数据随机(而 std 不需要)的算法能够通过,而且难以构造数据卡掉。如果你的输入文件确实非常大,可以考虑一下这么大的输入数据时不是必要的(小一点的输入行不行)。如果极大的输入仅仅是为了卡掉一个复杂度只是稍微劣一点的做法,你还可以考虑这种做法是否真的有卡掉的必要。
对于数据梯度,应该分层合适,最好让选手花在这个题上的时间与选手能得到的回馈(拿到的部分分)线性相关。对于签到题,部分分可以少一点;对于防 AK 题,部分分最好给多一点(除非你铁了心要恶心人了);对于捞分题(一般得到满分很难,定位为捞分的题目,例如省选联考题『骗分过样例』),一定要给十分充足、分档明确的部分分。
对于特殊性质的部分分,应到要谨慎设置特殊性质。一般一个特殊性质要能引导选手思考正解,而不是误导选手。
对于造数据,如果正解不依赖于数据随机的性质,一定要针对一些错误做法造出有一定强度,能卡掉错解的数据。不然就会重演我们这一届一个人出的 DP 被另外一个人的暴力记忆搜(状态数都超过了 的那种)+减枝干过去的历史(反复鞭尸)。
造好数据之后,最好要找个人帮你把题验了。
题目文件
- 题目的源文件和导出的 pdf。
- solution 源文件和导出的 pdf。
- std。
- generator,即数据生成器。
- validator,即数据校验器。即使不是 CF 赛制下的题目,我也建议写个 validator,不然可能会重现这个惨案。
- 数据,可能还包含
config.yml
或类似文件表示数据点得分(或 subtask)配置信息。 - 可能有的:checker 和 interactor,即 spj 和交互库。
造数据建议使用 testlib.h
造数据,而不是用 C++ 中的 rand
等随机函数。用 testlib
造数据时记得保留生成数据的参数(即 ./generator balabala
中的 balbabala
)。因为只要参数相同,无论何时,无论什么系统、什么版本的编译器,testlib
随机生成的数据都是相同的。这也使得数据可追溯化。
组比赛
如果你出好了很多高质量题目,迫不及待地准备组一场比赛,请注意以下几点:
- 题目的难度分配是否合理,题型是否合理(不要一场比赛三道通信题),整个比赛的题目是否含一定的区分度。
- (除非专题模拟赛)题目涵盖的算法是否较广。
- 整体码量是否合理,请不要出一套 ynoi。
- 换位思考,想一想别人打这场比赛的手感,会不会出现大部分人都坐牢的情况。
如果你组好了比赛,可以校内分享,也可以投给洛谷/Hydro/Codeforces/CodeChef 等等平台作为公开赛。注意如果你要出公开赛,就更应该每个题的题目质量,和比赛整体的难度分配。
- 标题: 出题!出题!出题!
- 作者: Arahc
- 创建于 : 2022-07-01 08:00:00
- 更新于 : 2023-03-29 11:54:00
- 链接: https://arahc.github.io/2022/07/01/【杂项】出题!出题!出题!/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。