简介:覆盖1992—2015年CTSC全国青少年信息学(计算机)奥林匹克竞赛的完整测试数据集与配套报告,主要面向冲击省选及全国决赛的信息学竞赛选手、教练与算法研究者,可作为历年真题数据复盘、对拍评测与命题风格分析的基准素材。压缩包共197个文件,约340.68MB,其中90个in输入文件、80个ans标准答案与20个out输出文件构成核心评测数据,另含Python、C++辅助脚本及PDF赛事报告,便于自动化对拍、结果核验与资料归档。已有207人学习下载,适合在NOI系列赛事备赛周期中用于模拟实战、检验算法正确性和梳理边界条件。借助这套资料,可系统获得历届CTSC测试数据、标准答案、输出样例、辅助统计脚本及赛事报告,大幅节省自行搜寻与整理数据的时间,为高水平算法训练提供可靠数据基础。
1. 一份1992年的RAR,凭什么让现在的信息学选手翻出来反复用
CTSC全国青少年信息学(计算机)奥林匹克竞赛测试数据集报告(1992-2015).rar,看着像旧档案,实际上装的是中国国家队选拔赛(CTSC,China Team Selection Contest)多年真题的测试点集合。CTSC负责在NOI之后再筛一轮,决定谁能代表中国出战国际信息学奥林匹克(IOI),参赛者全是国家队集训队成员,难度长期高过NOI,数据设计也出了名的“刁”,这让它在OI圈里一直有“高级题库”和“命题参考库”的双重身份。
这份数据包对三类人最有价值:备赛选手按年份把它当模拟赛做,教练和命题人从测试点结构反推得分点与数据强度,研究竞赛历史的人则能从文件规模变化看出题演进。接下来按实际操作顺序拆开讲:安全解压、编码修复、目录归一化、自测对拍,再单独列几个我在这份数据上实际翻车的点。
2. 认识CTSC测试数据集:竞赛位置、数据结构与二十年难度演进
2.1 CTSC在竞赛体系里的位置:为什么它的数据比NOI更“毒”
信息学竞赛的晋级链大致是:省级联赛与省选、全国决赛NOI、国家集训队,再往上就是CTSC国家队选拔赛,最后是IOI。CTSC一年一般两轮考试,参加人数只有几十人,全部是NOI金牌级别的选手。因为规模太小,CTSC的题目长期不在大众OJ上流传,官方也很少公开完整数据,这套1992-2015年的测试数据能攒成一个RAR在圈里传,本身就带点“民间文献”的气质。
CTSC的命题风格和NOI不同。NOI要照顾各省顶尖选手的区分度,CTSC面对的是已经能稳定拿NOI金牌的群体,常规算法题已经拉不开差距。所以命题人更喜欢靠数据强度来定名次:同样的算法,写得更干净、常数更小、边界处理更严的选手才能拿到极限数据的分。CTSC的测试点常常是“小样例给你对拍用,中等数据保证基础分,极限数据卡掉复杂度正确但常数大的写法”这种结构,这也是为什么它的测试数据对普通选手训练部分分策略特别有用。
2.2 一份CTSC数据的典型结构:目录组织、in/out配对与spj
我拿到过的历年CTSC数据,目录组织并不完全统一,但大致长这样:
CTSC2005/ ├─ problem_alpha/ │ ├─ alpha.in # 第1个测试点输入 │ ├─ alpha.out # 相应输出 │ ├─ alpha2.in │ ├─ alpha2.out │ └─ statement.pdf ├─ problem_beta/ │ ├─ beta_1.in # subtask 1 │ ├─ beta_1.out │ ├─ beta_2.in # subtask 2 │ ├─ beta_2.out │ ├─ spj.cpp # 特判题判断器 │ └─ beta.pdf有几个规律值得先说破。.in和.out是标准配对,但早期数据里答案文件用.ans后缀的情况不少,做自测脚本时必须兼容这两种命名。带spj或checker的题目说明它不是简单比对输出文件,而是需要跑特判程序,这种题用diff对拍会误判。还有部分题目会在子目录里单独放score配置文件或者每个测试点的分值说明,虽然这份RAR里不一定每道题都有,但遇到了一定先看它,分值分布往往比题面更直接地告诉你该拿哪些分。
2.3 数据规模的三次跳变:从几百字节到几兆字节
把1992到2015年的数据按年份排开,能清楚看到命题思路的演变。九十年代初的测试点通常只有五到八个,输入文件几百字节到几K,数据范围普遍是 n≤100,考的是递推、搜索和简单模拟,选手主要靠正确性拿分,部分分设计很粗糙。2000年到2008年前后,数据规模明显变大,单个测试点开始出现 10^5 甚至 10^6 的输入,规定了时间限制和空间限制,题目重心转向动态规划优化、图论、网络流和数据结构。
2009年之后是第三个节点:subtask概念成型,一个题目分成多个数据组,每组内部的数据范围一致、评分独立;交互题和提交答案题开始常态化,测试点数量动辄二三十个,单个.in文件可以到几兆字节。这二十年正好也是OI竞赛从“会做就行”到“做得快、写得稳、边界全”的转变过程。读懂这个演进,你拿到这份测试数据时就不会用同一套预期去对待1994年和2014年的题目。
3. 先把RAR摊开:三平台解压、完整性校验与GBK乱码修复
3.1 Windows用哪个RAR解压软件:7-Zip和WinRAR怎么选
在搜索引擎里输入“rar解压软件”,前排结果经常是各种下载站里的推广版,很多还捆绑安装器和弹窗广告。Windows下我一般只推荐两个选择:WinRAR官方版,或者7-Zip。WinRAR能压缩也能解压RAR,但试用期过了会弹购买提示,烦人但不影响核心功能;7-Zip免费开源无广告,解压RAR文件完全够用,缺点是它不能新建RAR格式,只能解压。对这个数据集来说,你只需要读取,所以7-Zip更省心。
下载时认准官方主站,不要从第三方下载站拿“特别版”“破解版”。这种打包过的RAR解压软件很容易夹带推广程序,我见过不止一次解压完数据,电脑多了一堆无关进程的情况。安装完先别急着解压,按下面顺序来:先用7z l看压缩包内部列表,确认没有异常再正式解压。
3.2 Linux和macOS命令行解压:7z、7zz与关键参数
Linux下推荐用系统软件源里的p7zip-full,或者直接装unrar。两个都装也不冲突。Debian/Ubuntu执行:
sudo apt update sudo apt install p7zip-full unrar -y 7z t CTSC_1992-2015.rar先跑7z t做完整性测试,它会逐个文件校验CRC,最后输出“Everything is Ok”才说明压缩包没损坏。如果某个文件报错,记下名字,先重新下载这个RAR,不要急着解压。测试通过后正式解压:
7z x CTSC_1992-2015.rar -o~/CTSC_raw注意-o和目标目录之间不能有空格,这是7-Zip命令行最容易翻车的地方。解压完立刻执行ls ~/CTSC_raw看首层目录。如果是分卷压缩包,形如.part1.rar、.part2.rar,只用解压第一个,7z会按序号自动读后续分卷。
macOS上我一般用Homebrew装sevenzip,装完后命令是7zz,用法与7z一致:
brew install sevenzip 7zz x CTSC_1992-2015.rar -o~/CTSC_raw3.3 文件名乱码的批量修复:convmv与Python脚本
解压完成后最常遇到的第一个问题:文件名全是乱码。原因很直接,压缩包在Windows下创建时,中文文件名按GBK/CP936编码写入;而Linux和macOS默认用UTF-8解码文件名,于是“二分图”变成“浜屽垎鍥”。先随机看几个文件名,确认是这种乱码再动手修。
Linux下最省事的修复工具是convmv:
sudo apt install convmv -y convmv -f gbk -t utf-8 --notest -r ~/CTSC_raw-f gbk指定源编码,-t utf-8指定目标编码,--notest的意思是真正执行改名,不加它会只打印预览结果。先不带--notest跑一遍看输出,确认改的都是乱码名,再正式执行。如果目录层级复杂或者你想自己控制逻辑,也可以写一个Python脚本,核心思路是用文件系统原始字节按GBK解码:
import os import sys def fix_dir(path): for name in os.listdir(path): old = os.path.join(path, name) raw = os.fsencode(name) # 拿到原始字节,不做解码假设 try: new_name = raw.decode('gbk') # 按GBK尝试解码 except UnicodeDecodeError: new_name = name # 解不通说明不是GBK,跳过 if new_name != name: os.rename(old, os.path.join(path, new_name)) print(f'rename: {name} -> {new_name}') # 处理改名后的子目录 if os.path.isdir(os.path.join(path, new_name)): fix_dir(os.path.join(path, new_name)) fix_dir(sys.argv[1] if len(sys.argv) > 1 else '.')注意这段脚本默认文件夹里外全是GBK编码。如果压缩包当初是用UTF-8打包的,强行按GBK解会把正常的名字改坏,所以执行前一定要先人工确认乱码形态,别批量误伤。
3.4 完整性校验与密码保护:先测RAR再谈“破解”
7z t通过不代表整个解压过程没问题,解压时偶尔仍会在某个文件上报CRC失败。如果出现这种情况,不要立刻去下载“RAR修复工具”。正确顺序是:先重新用7z t确认哪些文件坏了,再重新下载整个压缩包对比大小。RAR格式自带CRC校验,报错就是文件本体不完整,靠“修复”软件硬凑出来的文件即使能打开也是损坏数据,做题时答案对不上更浪费时间。
关于密码,网上搜“rar压缩包密码忘了强制解压”“rar密码移除”会看到一堆工具。RAR5的加密用AES-256,没有公开后门;这些工具实际做的只是字典穷举或者撞库,对强密码几乎无效,而且安装包很容易捆绑恶意程序。真正能做的就三件事:找发布者要密码;回忆自己设密码时的习惯词和变体;如果只是压缩包结构损坏而密码记得,用WinRAR或7-Zip的修复功能恢复文件本体。密码本身忘了且没有线索,直接放弃,别在暴力破解上耗时间。
4. 把1992-2015的数据变成训练集:目录归一化、对拍与subtask反推
4.1 目录归一化:用Python脚本统一历年命名
二十多年的数据,命名风格必然混乱:有的按拼音目录名,有的按英文题名,有的测试点叫1.in,有的叫problem_01.in,有的答案后缀是.out有的是.ans。统一成一套结构能省掉后面大量重复劳动,我习惯的规范是:
CTSC_clean/ ├─ 2005/ │ ├─ alpha/ │ │ ├─ data/ │ │ │ ├─ alpha_1.in │ │ │ └─ alpha_1.out │ │ └─ statement.pdf下面的Python脚本把解压后的原始目录扫一遍,复制出归一化结构,不改动原始文件:
import shutil from pathlib import Path src = Path('CTSC_raw') # 解压出来的原始目录 dst = Path('CTSC_clean') # 归一化后的输出目录 data_suffix = ('.in', '.out', '.ans') doc_suffix = ('.pdf', '.txt', '.html', '.htm') for year_dir in src.iterdir(): if not year_dir.is_dir(): continue year = year_dir.name for prob_dir in year_dir.iterdir(): if not prob_dir.is_dir(): continue pid = prob_dir.name # 题目标识直接用原始目录名 data_out = dst / year / pid / 'data' doc_out = dst / year / pid data_out.mkdir(parents=True, exist_ok=True) for f in prob_dir.iterdir(): if not f.is_file(): continue if f.suffix.lower() in data_suffix: shutil.copy(f, data_out / f.name) elif f.suffix.lower() in doc_suffix: shutil.copy(f, doc_out / ('statement' + f.suffix.lower())) print('done:', dst)这个脚本做了两件事:把测试点文件和题面文档分开存,同时把答案文件不区分.out还是.ans统一保留原名。为什么复制而不移动?因为原始RAR解压出来的目录可能还有你没注意到的生成器等文件,保留原样可随时回溯。运行前先确认src路径正确,输出目录不存在或已经清空,避免旧文件混入。
4.2 最小对拍脚本:用官方测试点验证本地代码
有了一致目录,自测就简单多了。先准备一个能自动跑所有测试点的Python对拍脚本:
#!/usr/bin/env python3 import subprocess import sys from pathlib import Path if len(sys.argv) != 3: print('usage: judge.py <program> <data_dir>') sys.exit(1) prog = sys.argv[1].split() # 程序命令,如 ['./main'] 或 ['python3', 'main.py'] data_dir = Path(sys.argv[2]) for in_file in sorted(data_dir.glob('*.in')): out_file = data_dir / (in_file.stem + '.out') if not out_file.exists(): out_file = data_dir / (in_file.stem + '.ans') if not out_file.exists(): print(f'{in_file.name}: NO ANSWER FILE') continue content = in_file.read_bytes() try: r = subprocess.run(prog, input=content, capture_output=True, timeout=10) except subprocess.TimeoutExpired: print(f'{in_file.name}: TLE') continue want = out_file.read_bytes().strip() got = r.stdout.strip() # 忽略输出末尾空白 if r.returncode != 0: print(f'{in_file.name}: RE, {r.stderr.decode(errors="replace")[:200]}') elif got == want: print(f'{in_file.name}: AC') else: print(f'{in_file.name}: WA')用法是对每个.in文件跑一次程序,和对应答案做字节级比较。timeout=10是单点的超时上限,CTSC老题时限一般宽松,10秒足够判断TLE。注意它假设所有测试点权重相同,只适合自己估分;遇到特判题(spj)时这套逻辑不成立,特判题要单独写判断逻辑,不能直接比对输出字节。
4.3 从测试点大小反推部分分:赛前拿数据做取舍
CTSC的测试点虽然多,但文件名和文件大小本身会透露信息。进入某道题的data目录,按输入文件大小排序:
cd CTSC_clean/2005/alpha/data for f in *.in; do printf "%-20s %8d bytes\n" "$f" "$(wc -c < "$f")" done | sort -k2n输出会很清楚:最小那几个文件通常是样例或者手算级的小数据,中间几个是中强度数据,最大的一两个是极限构造。按大小把测试点分组后,再对照题面给的subtask分值,你就能画出“哪组数据值多少分、要什么复杂度才能过”的图。
我训练时会强制自己只花十分钟做这一步,然后按“小数据拿稳、中数据冲一下、极限数据试水”分配写题时间。CTSC历史数据里很多题的极限点设计就是用来卡不完美实现的,知道自己哪些分拿不到,比闷头想正解更实在。
5. 避坑指南:RAR解压与数据使用中的5个常见问题
5.1 “7-Zip能解压RAR文件吗”:版本太老会报Unsupported method
现象:命令行执行7z x some.rar,输出里出现Unsupported Method或者Unsupported compression method,文件解不出来。
原因:RAR格式分RAR4和RAR5两代,旧版7-Zip(大概是15.12之前那一拨)对RAR5的支持不完整,遇到用新算法压缩的文件就会直接拒绝。
解决:升级7-Zip到当前最新版,Windows下打开7-Zip主界面点帮助里的检查更新,或者直接去官网下新安装包;Linux下sudo apt update && sudo apt upgrade p7zip-full,如果发行版源里版本太老,用新版7zip的Linux版本替代。升级后重新执行7z t再解压。这类问题不是RAR文件坏了,是解压器版本落后了。
5.2 “RAR密码移除”不可信:忘了密码时真正能做的三件事
现象:解压时提示输入密码,但发布者已经联系不上,或者你自己设的密码忘了。
原因:RAR的加密对RAR5是AES-256,对RAR4用AES-128,设计目标就是不可逆破解。那些标榜“rar密码移除”“强制解压”的软件,底层只是字典穷举或按掩码暴破,遇到稍微有点长度的密码基本等于没用。
解决:先冷静检查三件事。第一,回忆你所有用过的密码习惯,把可能的组合列出来手动试。第二,如果密码只有一位或者几位不对,可以试试有限掩码,比如知道是6位数字,穷举范围很小值得跑;完全不知道就别浪费时间。第三,找原始发布者问密码,这是最现实的路径。另一个容易忽略的点:部分老RAR只加密了文件列表,数据流没加密,这种包用WinRAR打开可能能看到文件名但解不出内容,判断它是否值得修之前先看压缩包属性。
5.3 CRC校验失败:修复恢复记录而不是乱下工具
现象:解压到一半弹出CRC错误,某个文件损坏,后面文件连续跟着报错。
原因:下载不完整、存放压缩包的U盘或硬盘有坏道、FTP传输中断过,都是常见来源。RAR文件有CRC校验位,任何一字节不对都会在解压时暴露。
解决:先用7z t全量测试,确认损坏范围是一个文件还是多个文件。如果只是个别文件坏,且这个RAR创建时带了恢复记录,用WinRAR的修复功能或rar r命令尝试重建;没有恢复记录的,修复成功率很低,最实际的办法是重新下载,下载后用7z t验证通过再解压。强烈不建议用各种“万能修复工具”——它们大多是拿正常文件顶替坏字节,看起来解压成功了,实际数据已经变了,做题时你会得到错误的答案却完全不知道问题出在数据上。
5.4 GBK乱码二次踩坑:别把本来就是UTF-8的文件名改坏
现象:解压后一部分文件名乱码,你按网上的教程用convmv -f gbk -t utf-8 --notest -r .跑完,原本正常的文件也变成乱码了。
原因:这个压缩包里混着两种编码的文件名。打包者在不同年份可能用了不同工具、不同系统,早期Windows里中文默认GBK,后来有些人用7-Zip打包时指定了UTF-8。一个目录里两种编码共存,全量转换必然误伤。
解决:转换前先做两步排查。第一,用7z l archive.rar看压缩包内部文件名,乱码形态是否一致。第二,解压后随机抽三层子目录看,确认乱码只有一种形态再批量转。如果已经转坏,并且你用--notest实际执行了改名且没备份,没有简单办法还原;所以在任何批量改名操作前,先整体复制一份,或者用convmv不带--notest跑一遍看预览。这一步是纯经验,我吃过亏,别省。
5.5 换行符CRLF与LF:一份AC代码换环境翻车
现象:本地Windows写好程序,测试点全过;同一份代码放到NOI Linux环境下编译运行,大面积答案错误或者输出多出字符。
原因:CTSC早期数据很多在Windows下生成,文本文件是CRLF换行。Linux程序读入时,如果用了fgets或逐字符读,会把行尾的\r也读进变量里,比较答案时自然对不上。程序在OJ上可能没暴露。但这份数据是你本地跑的,很容易踩。
解决:批量把数据文件转成LF,命令很简单:
sudo apt install dos2unix -y dos2unix CTSC_clean/1998/*/data/*.in CTSC_clean/1998/*/data/*.out更稳妥的做法是只在自测脚本里对读入做容错,比如用scanf、cin >>这类按空白分隔的读法,天然跳过\r。但如果你在写字符串逐行处理的题,一定先确认数据文件的换行符,别让环境差异浪费一晚上。
6. 进阶玩法:用CTSC历史数据反向训练做题手感与出题思路
把这份数据当普通题库刷完就停,其实亏了。我常用的一个进阶方法是“倒过来训练”:先不看题面,只对着输入输出想这题在考什么。打开某道题的几个测试点,观察输入文件大小和数据格式,猜算法类型;再跑一遍自己的程序,对比答案,看差值集中在哪些点。这个过程练的是读题能力里最抽象的“数据感觉”,N年真题喂下来,看到类似格式的题基本能猜个大概。
另一个更值钱的方向是拿老题做现代重制。CTSC早期很多题建模很好,但数据范围放在今天太小,当年要卡的是n=100的暴力算法,现在选手背模板都能过。把数据规模放大十倍、时限压到原来的四分之一,再要求自己写一版能过极限数据的实现,训练的就不是“背题”,而是复杂度分析和常数优化。每次改完数据别忘记录:原始数据范围、你改后的范围、你的耗时和翻车点,这比单纯刷题更容易看见进步。
我养成的习惯是每做完一年CTSC,在表格里记一行:年份、题目、算法标签、AC状态、实际耗时、失败原因。标签按动态规划、图论、数据结构、计算几何、交互题、提交答案题分类,积十几行后再看,你的弱点分布会非常明显——我当年就是靠这张表发现自己在计算几何上连续三年翻车,后来集中补了两个月。这份1992到2015的数据,真正难得的不是“老”,而是它横跨了竞赛命题从朴素到系统化的全过程,拿它做训练素材,至少要先把环境部分处理好,否则解压乱码和换行符问题就会消耗掉大半热情。希望这些步骤和踩坑记录帮到你,少走我走过的弯路。
本文还有配套的精品资源,点击获取