我们使用 MDX——一种 Markdown 的超集,并通过 XDM 编译器处理。它支持 HTML 和 React 组件,因此可以为每个模块添加交互功能和自定义组件。

前置元数据

前置元数据(Frontmatter)位于每个模块开头,由三条短横线包围。它使用 YAML 编写,用于保存模块的“元数据”。

  • ID必填。模块 ID,例如 getting-startedcontainers。它用于标识模块,因此必须唯一,并且只使用小写字母和短横线。网址会根据它生成。
  • 标题(Title)必填。模块标题,例如 Getting Started
  • 作者(Author)必填。模块作者,例如 Unknown
  • 贡献者(Contributors)可选。为模块贡献代码或简短说明的人。
  • 描述(Description)必填。模块的简短描述,类似 Codecademy 教学大纲中的描述。描述字段不支持 Markdown 或 LaTeX。
  • 前置知识(Prerequisites)可选。学习该模块前需要掌握的内容。如果要引用另一个模块作为前置知识,请填写其模块 ID,系统会自动生成链接。
  • 出现频率(Frequency)可选。取 0~4 的整数,其中 0 表示从未出现,4 表示大约每场比赛出现一次。如果不想显示频率,请省略该字段。
  • 重定向(Redirects)可选。一组会重定向到当前模块的网址。每当修改模块 ID 或将模块移动到其他组别时,都应添加重定向。

前置元数据示例

---
id: getting-started
title: Getting Started
description: Welcome to the guide! We'll introduce what programming competitions are and how this guide is organized.
author: Nathan Wang
order: 1
prerequisites:
 - Dummy prerequisite
 - running-cpp
redirects:
  - /silver/bipartite
---

# Getting Started
...

模块顺序

content/ordering.ts 文件保存模块顺序。其格式应当不言自明(通过 ID 进行匹配)。

链接到模块

在指南内部链接到另一个模块时,应使用以下格式:

[在此插入文字](/general/practicing)

在此插入文字

不要使用 practicing 这样的相对链接,否则会破坏我们的链接检查器

目录

系统会根据 Markdown 标题自动生成目录。设置模块格式时请牢记这一点。

MDX 与自定义组件

Optional: XDM 与 MDX

我们使用 XDM 编译器,它与 MDX v1 有一些区别:

  • 完全支持在 JSX 中穿插 Markdown;例如,<Info>some **markdown**</Info> 可以按预期工作。
  • 作为上一点的延伸,它完全支持缩进。可以缩进 JSX 标签内嵌套的 Markdown。这也意味着,用四个空格缩进文本不会使其变成代码块;请明确使用三个反引号包围代码块。
  • <> 需要用反斜杠转义,例如 \<

请注意,JSX 注释({/* ... */})与 Prettier 的配合不佳,因此请改用 HTML 注释。在内部,我们会先将 HTML 注释映射为 JSX 注释,再把 Markdown 交给 XDM。即使暂时无法理解全部细节,也不用担心。

以下组件在每个模块中都可全局使用,无需导入:

  • <Spoiler>
  • <Info>
  • <Warning>
  • <Optional>
  • <Problems>
  • <FocusProblem>
  • <Resources>
  • <Resource>
  • <TextTooltip>
  • <LanguageSection>
  • <CPPSection>
  • <JavaSection>
  • <PySection>
  • <CPPOnly>
  • <JavaOnly>
  • <PyOnly>
  • <IncompleteSection>
  • <Asterisk>
  • <Quiz>

下面会逐一说明这些组件。

折叠内容

折叠内容是只有用户点击后才会显示的可折叠元素,适合用于编写题目提示或解答。按照约定,模块题解不使用折叠内容。

<Spoiler title="Show Hint">
- Insert hint here
</Spoiler>

显示提示

信息块

<Info title="Insert Title Here">
**Markdown is Supported!!**
</Info>

在此插入标题

支持 Markdown!

警告块

<Warning title="Insert Title Here">
Fun fact: the title attribute is optional.
</Warning>
Warning: 在此插入标题

小知识:title 属性是可选的。

选学内容

<Optional title="Insert Title Here">
Fun fact: the title attribute is optional.
</Optional>
Optional: 在此插入标题

小知识:title 属性是可选的。

题目列表

每个模块对应两个文件:一个 .mdx 文件和一个 .problems.json 文件。.problems.json 保存该模块使用的重点题目与题目列表,同时还会被 Algolia 建立索引,用于题目搜索。

.problems.json 文件保存一个对象,其中键是题目列表名称(或重点题目名称),值是 ProblemMetadata 对象数组。对于重点题目,数组长度必须恰好为 1。此外,.problems.json 文件还应包含 MODULE_ID 键,其值为表示模块 ID 的字符串。

关于题目定义的更多信息,请参阅 src/models/problem.ts

用法示例:

<Problems problems="problems" />

[module].problems.json 应包含键 problems,其值对应一个 ProblemMetadata 数组。

ProblemInfoProblemMetadata 之间有所区别。[module].problems.json 中保存的是 ProblemMetadata。构建时,内容索引器会将 ProblemMetadata 转换为 ProblemInfo;React 组件在处理题目信息时使用 ProblemInfo。下面的文档介绍内容作者需要编写的 ProblemMetadata

ProblemMetadata 字段:

uniqueId——题目的唯一 ID。做题进度与此字段关联,因此不要修改它,否则进度会丢失。按照约定,其格式为 [source]-[SlugifiedProblemNameCamelCased]

  • 如果题目名称只有一个单词,该单词使用小写。
  • 如果题目来自 USACO 或 CSES,唯一 ID 分别使用 usaco-[USACO URL Number]cses-[CSES number]
  • 如果题目来自 Codeforces,唯一 ID 使用 cf-[contestNumber][problemLetter];如果来自 CF Gym,则使用 cfgym-[gymNumber][problemLetter]
  • 如果题目是带年份的 OI 题目,唯一 ID 使用 [oiName]-[twodigityear]-[slugifiedName]

以下是一些唯一 ID 示例:

cses-2177
poi-08-blockade
apio-18-duathlon
dmoj-investment
infoarena-xortransform
usaco-949
cses-1691
kattis-chineseremainder
cfgym-102538F
cf-1209H
spoj-LexicographicalStringSearch
ys-AssociativeArray

唯一 ID 相同的题目,其名称、来源和网址也应完全相同。

name——题目名称,不应包含来源。

示例:

2009 - Beetle
Greedy Pie Eaters
Zuma
2014 - The Stables of Genghis Khan

source——题目来源。可选值:待补充,请参阅 src/models/problem.ts 中的 contestsprobSources

difficulty——题目相对于所在模块的难度。可选值为 Very EasyEasyMediumHardVery HardInsane

isStarred——是否将题目标为重点。

tags——题目的标签列表。

solutionMetadata——题解信息。

export type ProblemMetadata = Omit<ProblemInfo, 'solution'> & {
  solutionMetadata:
    | {
        // auto generate problem solution label based off of the given site
        // For sites like Codeforces: "Check contest materials, located to the right of the problem statement."
        kind: 'autogen-label-from-site';
        // The site to generate it from. Sometimes this may differ from the source; for example, Codeforces could be the site while Baltic OI could be the source if Codeforces was hosting a Baltic OI problem.
        site: string;
      }
    | {
        // internal solution
        kind: 'internal';
      }
    | {
        // URL solution
        // Use this for links to PDF solutions, etc
        kind: 'link';
        url: string;
      }
    | {
        // Competitive Programming Handbook
        // Ex: 5.3 or something
        kind: 'CPH';
        section: string;
      }
    | {
        // USACO solution, generates it based off of the USACO problem ID
        // ex. 1113 is mapped to sol_prob1_gold_feb21.html
        kind: 'USACO';
        usacoId: string;
      }
    | {
        // IOI solution, generates it based off of the year
        // ex. Maps year = 2001 to https://ioinformatics.org/page/ioi-2001/27
        kind: 'IOI';
        year: number;
      }
    | {
        // no solution exists
        kind: 'none';
      }
    | {
        // for focus problems, when the solution is presented in the module of the problem
        kind: 'in-module';
        moduleId: string;
      }
    | {
        kind: 'sketch';
        sketch: string;
      };
};

题解也使用 MDX 编写,其前置元数据包含四个字段:

---
id: cses-1621
source: CSES
title: Distinct Numbers
author: Nathan Wang
---

... solution

题解前置元数据中的 ID 必须与题目的唯一 ID 相同。还要确保将所有相关题目的 solutionMetadata 中的 kind 更新为 'internal'。我们假定只要存在站内题解,就应当使用它;因此,如果存在站内题解,但 solutionMetadatakind 未设为 'internal',构建就会报错。添加题解模块介绍了如何添加新题解。

重点题目

将一道题显示为“重点题目”。

<FocusProblem problem="genPermutations" />

[module].problems.json 应包含键 genPermutations,其值对应一个长度为 1 的数组

资源列表

<Resources>
  <Resource
    source="Errichto"
    title="Video - How to test your solution"
    url="https://www.youtube.com/watch?v=JXTVOyQpSGM"
    starred
  >
    using a script to stress test
  </Resource>
  ...
</Resources>
Resources
Errichto

使用脚本进行压力测试

根据来源提供的特殊功能:

  • 如果来源是一本书,系统会自动将网址设为该书的链接。
    • 支持的书籍:
      • GCP(Guide to Competitive Programming)
      • CPH(Competitive Programming Handbook)
      • PAPS(Principles of Algorithmic Problem Solving)
      • PAPS1(Principles of Algorithmic Problem Solving)
      • CP2(Competitive Programming 2)
      • IUSACO(Darren 的书;会根据用户选择的语言自动设置网址;Python 用户使用 C++ 版本)
  • 对于某些来源,系统会自动生成工具提示(列表见此处)。

工具提示

工具提示主要有两类:文本工具提示会在文字下方显示虚线;星号工具提示则渲染一个可悬停查看的星号。

<TextTooltip>

<TextTooltip content="Popup text goes here">short text goes here</TextTooltip>
在此填写简短文字

<Asterisk>

<Asterisk>Popup text goes here</Asterisk>

未完成章节

<IncompleteSection>

- this list is optional and can be used to specify what is missing
- missing 32-bit integer explanation

</IncompleteSection>

This section is not complete.

Any help would be appreciated! Just submit a Pull Request on GitHub.
  • 此列表可选,可用于说明缺少哪些内容
  • 缺少 32 位整数的说明

代码块与代码片段

与普通 Markdown 一样,代码块由三个反引号分隔。此外,我们还支持可折叠代码片段。下面是用法示例:

带标题

// Before
// BeginCodeSnip{Optional Code Snippet Title}
// Code snippet goes here
// You can indent the entire BeginCodeSnip block (including the BeginCodeSnip line) and it will function as expected
// EndCodeSnip
// After
// Before
Code Snippet: Optional Code Snippet Title (Click to expand)

不带标题:

// BeginCodeSnip{}
// My snippet code goes here
// EndCodeSnip
// My non-snippet code goes here
Code Snippet (Click to expand)
// My non-snippet code goes here

Kattio 有一种简写方式,定义在 src/mdx-plugins/rehype-snippets.js 中,通过 CodeSnip{Kattio} 使用。

Kattio

CodeSnip{Kattio} 会被替换为相应缩进的版本(缩进量取决于 CodeSnip 的缩进):

import java.io.*;
import java.util.*;
public class myClass {
Code Snippet: Kattio (Click to expand)
public static void main...
}

特定语言内容

<LanguageSection>
<CPPSection>
#### A heading that only appears in C++

```cpp
C++ code here
```

</CPPSection>
<JavaSection>
#### A heading that only appears in Java

```java
Java code here
```

</JavaSection>
<PySection />
</LanguageSection>

在上面的示例中,Python 不会渲染任何内容。

如果只想为一种语言渲染内容,使用 CPPOnlyJavaOnlyPyOnly 会更加方便:

<CPPOnly>
#### A heading that only appears in C++

```cpp
C++ code here
```

</CPPOnly>

C++

只在 C++ 中显示的标题

C++ code here

Java

Python

测验

<Quiz>
	<Quiz.Question>
		Binary search

		<Quiz.Answer>
			$O(\log n)$

			<Quiz.Explanation>
				Almost. Prefer $\mathcal{O}$ over $O$.
			</Quiz.Explanation>
		</Quiz.Answer>

		<Quiz.Answer correct>
			$\mathcal{O}(\log n)$

			<Quiz.Explanation>
				That's correct!
			</Quiz.Explanation>
		</Quiz.Answer>
		<Quiz.Answer>
			O(log n)

			<Quiz.Explanation>
				That's not right. Latex is important...
			</Quiz.Explanation>
		</Quiz.Answer>
	</Quiz.Question>
	<Quiz.Question>
		```cpp
		for (int i = 0; i < 100; i++) {
			for (int j = 0; j < m; j++) {
				// constant time code here
			}
		}
		```

		<Quiz.Answer>
			$O(100m)$

			<Quiz.Explanation>
				That's not correct. Constant factors are ignored.
			</Quiz.Explanation>
		</Quiz.Answer>
		<Quiz.Answer correct>
			$O(m)$

			<Quiz.Explanation>
				That's correct!
			</Quiz.Explanation>
		</Quiz.Answer>
	</Quiz.Question>
</Quiz>

二分查找

Question 1 of 2

Module Progress: