
简介这份NOI 2024基础知识题库PDF面向备战全国青少年信息学奥林匹克竞赛的选手及指导教师针对笔试环节容易失分的基础概念与操作规则进行系统整理并提示笔试一般包含45道单选题和5道多选题。内容覆盖Linux竞赛环境、文件与进程管理、vim编辑、g编译选项、程序调试及输入输出重定向具体涉及mv重命名、cd..返回上级、rm -r删除目录、./a.out运行程序、gdb调试、killall结束进程、ls -a查看隐藏文件、time测量耗时以及vim中:q!、:wq、:12、/12等操作并延伸至考试登录、文件命名、源码大小限制、评测与复评流程等细节方便赛前集中速查与查漏补缺。资源包内共1个PDF文件大小约382KB轻量便携适合打印或离线查阅。目前已有333人学习下载适合初、中级选手在冲刺阶段巩固NOI笔试范围内的高频考点、命令用法与考场规则也可作为教练组织赛前模考与知识梳理的参考材料。1. 一份能用的 NOI 2024 基础知识题库难点从来不在搜集赛前两周选手硬盘里摞着几十份历年真题的 PDF文件名从NOI2019_day1.pdf、NOIP2021.pdf一路到新建文件夹(3).pdf。真正卡住他的不是资料太少而是他明明记得某道题考过基环树却想不起它落在哪一年的哪一份文件、第几页想按知识点刷一遍只能一份份翻。NOI 2024 基础知识题库 要解决的正是这件事把散落的真题、考纲知识点、模板代码从一堆文件变成一张能按知识点查、按难度排、按年份追溯的索引表再导出成一份带书签和目录的 PDF。适合动手做这套整理的是带队教练、自学冲省选和 NOI 的选手以及想把训练流程工程化的人。边界也说清楚这是资料整理的活不是题解搬运来源标注和版权信息该留的都得留。下面按采集、打标、导出、复盘四步走一遍。2. 历年真题的结构化采集从 PDF 到可检索字段动手之前先定字段字段定不下来后面写多少脚本都是白费——你会不断发现少了时限、少了子任务分、少了考察算法回头再补一次全量重跑。采集阶段的目标只有一个让每一道题变成一条独立记录题面、样例、限制条件都在记录里文件只是原始素材。2.1 真题文件的命名规范与目录分层目录按赛事 / 年份 / 阶段三层切文件名只保留题号其余信息全部落到 JSON 里。这样做的收益在后面很明显换来源网页抓的、别人发的 docx、扫描件时路径规则不变脚本不用改。层级示例约定说明赛事raw/NOI/简写统一大写NOI、NOIP、WC省选写PROV年份raw/NOI/2024/用四位年份跨年赛季以正式比赛年份为准阶段raw/NOI/2024/day1/多试题目录单场考试写single题目raw/NOI/2024/day1/T1.pdf只留题号中文标题不进文件名避开编码问题扫描件要单独处理先判断page.extract_text()返回的是不是空串是空串说明这一页没有文本层得先走 OCR 再进入后面的流程。这一步不提前分拣后面正则匹配会静默失败样例丢了你都不知道。2.2 用 pdfplumber 抽出题面、时限与样例数据下面的脚本把一份题目 PDF 拆成结构化字典解析失败时保留raw_text方便人工兜底。# parse_problem.py import json import re from pathlib import Path import pdfplumber TIME_RE re.compile(r时间限制[:]\s*(\d(?:\.\d)?)\s*(ms|s|秒)) MEM_RE re.compile(r内存限制[:]\s*(\d)\s*(MB|MiB|mb)) SAMPLE_RE re.compile( r输入样例\s*#?\s*(\d)[:]?\s*\n([\s\S]*?)\n\s*输出样例\s*#?\s*\1[:]?\s*\n r([\s\S]*?)(?\n\s*(?:输入样例|样例输入|提示|数据范围|$)) ) def extract(path: Path) - dict: 把一份题目 PDF 解析成字段样例编号用反向引用配对避免串位。 pages [] with pdfplumber.open(path) as pdf: for page in pdf.pages: # layoutTrue 保留原始空格缩进样例数据的层级关系靠它还原 txt page.extract_text(layoutTrue, x_tolerance2, y_tolerance3) pages.append(txt or ) raw \n.join(pages) samples [ {index: int(m.group(1)), input: m.group(2).strip(), output: m.group(3).strip()} for m in SAMPLE_RE.finditer(raw) ] time_m, mem_m TIME_RE.search(raw), MEM_RE.search(raw) return { source: str(path), raw_text: raw, time_limit_s: ( float(time_m.group(1)) / (1000 if time_m.group(2) ms else 1) if time_m else None ), memory_limit_mb: int(mem_m.group(1)) if mem_m else None, samples: samples, } if __name__ __main__: for p in sorted(Path(raw).rglob(*.pdf)): rec extract(p) out Path(build/records) / p.relative_to(raw).with_suffix(.json) out.parent.mkdir(parentsTrue, exist_okTrue) out.write_text(json.dumps(rec, ensure_asciiFalse, indent2), encodingutf-8) print(f{p} - {len(rec[samples])} 组样例, 时限 {rec[time_limit_s]})关键参数有三个。layoutTrue打开后输出会按页面坐标补空格样例里的多行输入不会挤成一行x_tolerance、y_tolerance控制字符合并成词的宽容度默认 3 在正常排版的题面上够用遇到双栏的旧题面调到 2 更稳。样例正则里的\1是反向引用保证输入样例 2匹配到的必须是输出样例 2否则多组样例会错配。若某份 PDF 抽出来的samples为空而题面明显有样例优先怀疑是图片式样例回到 OCR 分支。2.3 题目标签字段设计与 JSON 结构记录结构建议一次定死后面所有工具都围着它转。{ id: NOI2024-D1-T1, year: 2024, contest: NOI, stage: day1, index: 1, title: 双序列扩展, time_limit_s: 1.0, memory_limit_mb: 512, knowledge_points: [动态规划, 数据结构/树状数组], difficulty: 0.42, samples: [{index: 1, input: 3 2\n1 2 3\n4 5 6, output: 12}], source_file: raw/NOI/2024/day1/T1.pdf }字段类型采集方式备注idstring拼接生成赛事年份阶段题号全局唯一用作 PDF 锚点time_limit_sfloat正则毫秒统一折算成秒方便排序memory_limit_mbint正则老题只写 MB新题可能有 MiB注意区分knowledge_pointsarray自动初标人工复核值必须来自固定词表difficultyfloat人工或通过率映射0~1越大越难samplesarray正则至少保留一组用于后续校验3. NOI 基础知识题库的知识点分级与标签体系字段齐了接下来最费人力的环节是打标。一套 NOI 基础知识题库 能不能用起来几乎全看知识点树切得够不够细又不至于碎到每个标签只有一道题——那种标签等于没有。3.1 从语法到算法的知识点树建议一级控制在 8 到 10 个二级 40 到 60 个三级基本靠题目里的特殊性质描述体现不再单列标签。一级二级示例典型考察信号语言与基础模拟、枚举、高精度数据范围小、题意直接数据结构并查集、树状数组、线段树、单调栈出现区间修改动态维护图论最短路、拓扑排序、基环树、网络流出现边权环上匹配动态规划线性 DP、背包、区间 DP、状压、树形 DP出现方案数最大值且状态可压缩数学数论、组合计数、概率期望、矩阵出现取模逆元期望字符串KMP、哈希、Trie、后缀数组出现模式串前缀计算几何凸包、叉积、旋转卡壳出现坐标面积共线顺序上按语言—数据结构—图论—DP—数学排就行它跟多数人的训练路径一致做索引页时也好看。同一个知识点在不同年份的叫法要统一比如树上差分别一处写树差分否则频次统计会散成两条。3.2 关键词初标加人工复核自动打标不要上太重的模型先用关键词规则跑一遍命中数排前二的标签作为初标人工只看初标为空或冲突的记录工作量能压到原来的三分之一。# tag_problem.py import json from collections import Counter from pathlib import Path RULES { 图论/最短路: [最短路, dijkstra, spfa, 边权, 松弛], 图论/基环树: [基环树, 环上, 内向树, 外向树], 数据结构/线段树: [线段树, 区间加, 区间查询, 懒标记], 动态规划/状压DP: [状压, 二进制表示状态, n 20, n20], 数学/数论: [逆元, 欧拉函数, 同余, 质因数], } def tag(rec: dict, threshold: int 2) - list[str]: text rec[raw_text].lower() score Counter() for label, keys in RULES.items(): for k in keys: score[label] text.count(k.lower()) # 阈值 2只在题面顺口提一句“最短路”的题不该被打上图论标签 return [label for label, cnt in score.most_common(2) if cnt threshold] if __name__ __main__: for p in Path(build/records).rglob(*.json): rec json.loads(p.read_text(encodingutf-8)) rec[knowledge_points] rec.get(knowledge_points) or tag(rec) p.write_text(json.dumps(rec, ensure_asciiFalse, indent2), encodingutf-8)threshold是这套规则里最需要调的参数调到 1 会大量误标调到 3 又会让只用一句话描述算法的题漏标。经验值是 2配合最多取前两个标签的上限能压住一题挂五个标签的情况。词表本身要持续维护遇到复核时人工补的标签把新词回写进RULES下一轮自动初标就更准。3.3 用 SQL 统计知识点频次指导刷题顺序标完不等于结束得知道哪些知识点是真的高频。把 JSON 灌进 SQLite用json_each把数组展开成行再聚合。-- 依赖 SQLite 3.38 的 json_each把 knowledge_points 数组展开成行 CREATE VIEW IF NOT EXISTS v_kp AS SELECT r.year, r.contest, r.id, r.difficulty, j.value AS kp FROM records r, json_each(r.knowledge_points) AS j; -- 每个知识点被考过几次、平均难度多少、分布在哪些年份 SELECT kp, COUNT(*) AS times, ROUND(AVG(difficulty), 2) AS avg_diff, GROUP_CONCAT(DISTINCT year) AS years FROM v_kp GROUP BY kp HAVING times 2 ORDER BY times DESC, avg_diff DESC;HAVING times 2是为了滤掉偶发标签只看重复考察的知识点avg_diff高而times也高的行就是你该优先安排专题训练的位置。这张结果表还能直接拿去做 PDF 索引页比按年份排的目录有用得多。4. 把整理结果导出成 PDFMarkdown 加 LaTeX 的可复现流水线记录攒齐之后才轮到排版。很多人直接开 PDF 编辑器手动加书签题目一上百就崩改一道题要重新点几十次。更省事的路子是内容用 Markdown 管出稿交给 pandoc 加 XeLaTeX改一道题只重跑一条命令。4.1 文档骨架与一次能出书签的转换命令主文档只放章节引用具体题目拆成独立文件方便增量编译。--- title: NOI 基础知识题库与历年真题整理2024 版 documentclass: ctexbook classoption: oneside toc: true toc-depth: 3 numbersections: true geometry: margin2.2cm header-includes: | \usepackage{booktabs} \usepackage{tcolorbox} \newtcolorbox{samplebox}{colbackgray!6,colframegray!50,boxrule0.4pt} --- \mainmatter # 一、语言与基础 !INCLUDE chapters/01-basic.md # 二、数据结构 !INCLUDE chapters/02-ds.mdpandoc build/book.md \ --pdf-enginexelatex \ -V CJKmainfontNoto Serif CJK SC \ -V monofontJetBrains Mono \ --highlight-styletango \ --toc --toc-depth3 \ --number-sections \ -o build/NOI-2024-题库.pdfCJKmainfont必须指定一个系统里真实存在的字体名写错会直接报字体找不到而不是中文乱码这是最容易浪费半小时的坑--toc-depth3决定书签层级题号挂在第 3 层刚好再深 PDF 阅读器的侧栏会显得很挤--highlight-style让代码段有稳定的配色导出后仍可选中复制。如果只想先看排版效果用浏览器打印到 PDFWindows 上常见的 Microsoft Print to PDF 驱动也能出稿但书签层级和页码交叉引用不如 LaTeX 稳正式版本还是走这条链路。4.2 中文字体、代码高亮与样例框的几个必调参数样例是最容易排乱的部分用tcolorbox包一层输入输出分色打印出来对照调试也清楚。% header.tex —— 通过 --include-in-headerheader.tex 引入 \usepackage{xeCJK} \setCJKmonofont{Noto Sans Mono CJK SC} \setlength{\parindent}{0pt} % 技术文档不首行缩进段落间距代替 \setlength{\parskip}{0.6em} \usepackage{fancyhdr} \pagestyle{fancy} \fancyhead[L]{\small NOI 基础知识题库} \fancyhead[R]{\small\leftmark} % 页眉右侧跟随当前章标题 \renewcommand{\headrulewidth}{0.4pt}参数建议值作用与影响parindent0pt技术文档用段间距分块缩进会和代码块混淆parskip0.6em配合上一项太大会明显拉长页数CJKmainfont系统中已有衬线中文字体缺失字体会中断编译不报乱码toc-depth3题号在第 3 层深一层侧栏就太挤numbersections开启题号与章节号都能被交叉引用4.3 按年份和知识点批量导出只重编改过的文件全量重编几百页很慢用文件哈希做缓存只重跑改动过的分册。#!/usr/bin/env bash set -euo pipefail CACHEbuild/.hash mkdir -p $CACHE build/pdf for md in chapters/*.md; do name$(basename $md .md) now$(sha1sum $md | cut -d -f1) old$(cat $CACHE/$name 2/dev/null || echo ) if [ $now $old ]; then echo skip $name continue fi pandoc $md --pdf-enginexelatex \ -V CJKmainfontNoto Serif CJK SC \ --include-in-headerheader.tex \ -o build/pdf/$name.pdf echo $now $CACHE/$name done脚本按章节切分册改一个知识点只重编一册哈希文件放在build下清缓存时顺手删掉即可。分册还有一个好处按知识点导出的单册可以直接发给学生当专题练习不用把整本题库传过去。5. PDF 题库的检索、校验与刷题复盘5.1 用 SQLite FTS5 建全文索引秒查知识点的全部出处PDF 翻页再快也不如查询。把题面正文灌进 FTS5 虚表查一个关键词能直接列出年份、题号和页码。CREATE VIRTUAL TABLE IF NOT EXISTS problem_fts USING fts5(id UNINDEXED, title, body, tokenizeunicode61); INSERT INTO problem_fts(id, title, body) SELECT id, title, raw_text FROM records; -- 查“基环树”出现在哪些题里按年份倒序看演变 SELECT id, title FROM problem_fts WHERE problem_fts MATCH 基环树 ORDER BY id DESC LIMIT 20;tokenizeunicode61对中文是逐字切分所以基环树这类词直接当短语查是可行的如果词表里有多字术语也可以用MATCH 最短路 OR dijkstra组合查询。索引表建在build/下重新采集后重建即可不必进版本库。5.2 样例数据一致性校验先确认题面没被解析歪整理过程中最隐蔽的错误是样例被截断——比如多行输入只抽到第一行人工扫一眼根本看不出来。写个小脚本把每题第一组样例的输入输出行数打印出来异常值一眼可见。import json from pathlib import Path for p in sorted(Path(build/records).rglob(*.json)): rec json.loads(p.read_text(encodingutf-8)) if not rec[samples]: print(f[缺样例] {rec[source]}) continue s rec[samples][0] if len(s[input].splitlines()) 1 and len(s[input]) 60: print(f[疑似截断] {rec[source]} 首行长度 {len(s[input])})判据是单行且特别长正常样例极少出现这种情况出现基本都是多行被合并。逐条修完再重编 PDF比事后在 PDF 编辑器里手动改省事得多。5.3 把知识点索引页和 PDF 书签钉在一起最后一步是让整本 PDF 可跳转。用 pandoc 的--toc生成的目录只到章节题目级别跳转要靠 LaTeX 的\hypertarget在每道题开头插一行锚点锚点名就用记录里的id。\hypertarget{NOI2024-D1-T1}{} \section*{T1 双序列扩展}配合hyperrefctexbook 默认已加载这条锚点既能被侧栏书签索引到也能被外部链接命中。生成索引页时把 3.3 节那张频次表按avg_diff倒序输出成一张三列表格每行链接到对应题目锚点放在正文之前以后每加一年真题只要重跑采集—打标—导出三步索引页的页码和链接由xelatex自动重算不用手动维护。本文还有配套的精品资源点击获取