はじめに
こんにちは。kit84 (AtCoder 緑) です。
最近、競技プログラミングを始めたのですが、いろいろと学びがあって楽しいですね。
私も何か競プロ記事を書いてみようと思い立ち、遂にブログを生やしました。
生暖かい目でご覧ください。
地の文
数式交じりの文の解説記事では、「数式」にフォーカスが当てられがちで、文章自体のフォーマットに関する記事はほとんど見ません。
競プロの問題文を読みやすいものにするために、役に立ちそうな Tips をまとめておきます。
段落内改行は行末にスペース 2 つ
競プロの問題文は、頻繁に改行したくなるものです。 でも、普通に改行しても表示上は無視され、改行になりません。 だからといって空行を入れると、段落分けになってしまいます。
どうすればよいでしょうか? 答えは、「行末にスペースを 2 つ置く」です。
(マークダウンの仕様です。)なぜ誰も教えてくれなかったのか……
input
> この行の末尾に空白なし。 > この行の末尾に空白あり。 > あかさたなはまやらわ。
output
この行の末尾に空白なし。 この行の末尾に空白あり。
あかさたなはまやらわ。
note
行末のスペースが 1 つ以下の場合は、ホワイトスペースが無視されます。
HackerRank の場合、引用文の内側では段落分けができないようです。(?)
文中数式の埋め込みは \\( \\)
文中のあらゆる数式、変数名、数字は \\( \\)
で囲みます。
HackerRank の場合、バックスラッシュは 2 個必要です。代わりにドル記号 $ $
でもよいです。
独立行形式の数式は、\\[ \\]
, $$ $$
です。
数字や数式の前と後ろにはスペースを入れましょう。(くっつけると表示が変です。)
input
> \\(H\\)行\\(W\\)列の格子が\\(1\\)個ある。 > \\(H\\) 行 \\(W\\) 列の格子が \\(1\\) 個ある。
output
\(H\)行\(W\)列の格子が\(1\)個ある。
\(H\) 行 \(W\) 列の格子が \(1\) 個ある。
note
スペースを入れない場合、かなり詰まって見えてしまいます。
余裕を持たせたほうが読みやすく、見映えがよいです。
入力形式は引用文で
引用文 (> ほげ
) を使ってあげると背景色がつくので見やすいです。(あくまで HackerRank 環境の場合)
一方、コード片 (`ほげ`
) を使うと MathJax が効きません。
input
> \\(N\ \ D\\) > \\(A_{1,1}\ \ A_{1,2}\ \ \cdots \ \ A_{1,D}\\) > \\(\ \\, \vdots\\) > \\(A_{N,1}\ \ A_{N,2}\ \ \cdots \ \ A_{N,D}\\)
output
\(N\ \ D\)
\(A_{1,1}\ \ A_{1,2}\ \ \cdots \ \ A_{1,D}\)
\(\ \, \vdots\)
\(A_{N,1}\ \ A_{N,2}\ \ \cdots \ \ A_{N,D}\)
note
1 行ごとに数式を分割すると楽です。
改行が必要な場合は、行末(数式の外)にスペースを 2 個置きます。
隠し味として、\vdots
の手前にも適度に空白を入れておくと見映えがいいです。
しかし、 \,
(微小な空白)が壊れることがありますね。\\,
にすると大丈夫だったりします。(HackerRank は何かと罠があって、試行錯誤が要求される)
制約は箇条書きで
箇条書きは、行頭を -
にします。
input
> - 入力はすべて整数 > - \\(1\le N\le 10^5\\) > - \\(1\le D\le 8\\) > - \\(0\le A_{i,j} \le 10^9 \quad\\) \\((1\le i\le N,\,\ 1\le j\le D)\\)
output
- 入力はすべて整数
- \(1\le N\le 10^5\)
- \(1\le D\le 8\)
- \(0\le A_{i,j} \le 10^9 \quad\) \((1\le i\le N,\,\ 1\le j\le D)\)
note
(別に引用文にする必要はないのですが、引用文の中でも箇条書きができます。)
添字の範囲を書くカッコの手前には、\quad
で空白を入れましょう。
また、カンマで数式を区切るときは、カンマの後に空白を入れると見やすいです。
数式が横に長くなると、モバイル環境などで画面からハミ出すことがあるので、適度に分割すると良い感じに改行してくれます。
画像
画像ボタンを押して、画像を貼ります。
input
![image](https://s3.amazonaws.com/hr-assets/0/1576162900-e685ba7f3a-E26E96F9-1DDE-4F93-B0EF-03C168B729DD.png)
output
note
画像が大きすぎましたね。
この画像は何かというと、水筒を傾けるやつ の反省画像です。
数式
数式で「出したい記号があるけど出し方が分からない!」という場合は、検索すれば分かるので(→ 参考サイト)いいとしても、「なんとなく書ける間違った書き方」という厄介パターンが割とあります。
ここでは、競プロで使いがちな例をピックアップして紹介します。
乗算記号は \times
プログラマは乗算の意味で \(*\) を使うことに抵抗はないと思うのですが、本来は乗算の記号ではないです。(畳み込みとかの意味で使うことがある)
input | output | note |
---|---|---|
a*b |
\(a*b\) | 好ましくない (文脈によると思います) |
a\times b |
\(a\times b\) | 乗算、外積 |
a\cdot b |
\(a\cdot b\) | 乗算、内積 |
ab |
\(ab\) | だいたい省略しますよね |
関係・比較演算子
\ll
( \(\ll\) )とか \gg
( \(\gg\) )まで覚えておくと、かっこいい文章が書けます。
\leqq
( \(\leqq\) )とかを使うと横棒 2 本になりますが、フォント的にあまり美しくありません。
\leqslant
( \(\leqslant\) )も… 私は好きですが、全然見かけませんね。
input | output | note |
---|---|---|
a<b |
\(a\lt b\) | \lt も同じ |
a>b |
\(a\gt b\) | \gt も同じ |
a\le b |
\(a\le b\) | \leq も同じ |
a\ge b |
\(a\ge b\) | \geq も同じ |
a\ll b |
\(a\ll b\) | a<<b (\(a<<b\))はダメ |
a\gg b |
\(a\gg b\) | a>>b (\(a>>b\))はダメ |
a=b |
\(a=b\) | 等号 |
a\ne b |
\(a\ne b\) | \neq も同じ |
a\not\equiv b |
\(a\not\equiv b\) | 否定は \not と組み合わせる |
a\fallingdotseq b |
\(a\fallingdotseq b\) | 日本人がよく使う |
a\approx b |
\(a\approx b\) | 海外でも使う |
関数としての文字
\log
, \max
みたいなやつ。\
をつけるだけ。
これは、最低限のルールです。 \
をつけているかどうかで、その文章のレベル感がバレてしまいます。
input | output | note |
---|---|---|
O(N log max A) |
\(O(N log max A)\) | いやなきもち (全文字の積になる) |
O(N\log\max A) |
\(O(N\log\max A)\) | よさそう |
\tan^{-1}x |
\(\tan^{-1}x\) | アークタンジェント |
\arctan x |
〃(あるものは使おう) | |
\mathop{\rm atan}x |
\(\mathop{\rm atan}x\) | 〃(無いものは作ろう) |
a\mathbin{\rm xor}b |
\(a\mathbin{\rm xor}b\) | 排他的論理和 (二項演算子として) |
二項係数
nCk、入力が面倒なことであまりにも有名。
一方、\binom NK
だと \(\binom NK\) になってしまい、中学・高校では馴染みがない感じです。
input
\\[ \def\nCk#1#2{ {}_ {#1}\mathrm{C}_ {#2} } \nCk{N}{K} \\]
output
\[ \def\nCk#1#2{ {}_ {#1}\mathrm{C}_ {#2} } \nCk{N}{K} \]
note
上記のようにマクロを 1 回宣言しておけば、以降、同一ファイル内では \nCk ab
( \(\nCk ab\) ) のように繰り返し用いることができます。
ここで、アンダーバー _
が 2 回出現した場合の挙動がおかしい(マークダウンの下線機能と衝突する)ので、あとに空白を入れて回避しています。
切り上げ・切り捨て
ガウス記号 [ ]
を floor 関数の意味で使うのは、混乱を招くのでよくないです。
\left\lfloor
, \right\rfloor
みたいなやつを使います。
ついでに絶対値も。
input | output | note |
---|---|---|
\lceil \cfrac ab \rceil |
\(\lceil \cfrac ab \rceil\) | はみだしちゃう |
\left\lceil \cfrac ab \right\rceil |
\(\left\lceil \cfrac ab \right\rceil\) | 切り上げ |
\left\lfloor \cfrac ab \right\rfloor |
\(\left\lfloor \cfrac ab \right\rfloor\) | 切り捨て |
\left| \cfrac ab \right| |
\(\left\vert \cfrac ab \right\vert\) | 絶対値 |
縦棒 |
は \vert
と同じです。
縦棒が出てきたので。
「 \(a\) は \(b\) を割り切ります」ということを、記号 a\mid b
で表すことがあります。(→ 参考)
input | output | note |
---|---|---|
a|b |
\(a\vert b\) | にてるけどちがうよー |
a\not| b |
\(a\not\vert b\) | にてるけどちがうよー |
a\mid b |
\(a\mid b\) | 割り切る |
a\not\mid b |
\(a\not\mid b\) | 割り切らない |
a\nmid b |
\(a\nmid b\) | 割り切らない |
なお、私は使ったことがありません。(どっちが割る方か忘れてしまうため)
剰余
よくある mod ですが、なぜか 3 種類も用意されています。
間隔が空かない \bmod
は、二項演算子としての使用を意図しています。
input | output | note |
---|---|---|
a=p mod 10^9+7 |
\(a=p mod 10^9+7\) | これはいけない |
a \equiv p \mod 10^9+7 |
\(a\equiv p\mod 10^9+7\) | 合同式用の間隔が空く |
a \equiv p \pmod{10^9+7} |
\(a\equiv p\pmod{10^9+7}\) | 括弧付き |
a \equiv p \bmod 10^9+7 |
\(a\equiv p\bmod 10^9+7\) | 間隔が空かないのでダメ |
一般記号の \(\%\) を二項演算子として使いたい場合は、 \mathbin{\%}
とすると良いでしょう。
input | output | note |
---|---|---|
a\mod b |
\(a\mod b\) | これは片寄るのでダメよ |
a\bmod b |
\(a\bmod b\) | 剰余をとる演算 |
a\% b |
\(a\% b\) | % の前には \ が必要 |
a\mathbin\% b |
\(a\mathbin\% b\) | 二項演算子用の間隔が空く |
大きい数は {,}
で桁区切りします。普通にカンマを使うと、離れすぎてしまいます。
input | output | note |
---|---|---|
998244353 |
\(998244353\) | よく見る素数 |
998,244,353 |
\(998,244,353\) | 998 と 244 と 353 という 3 つの数 |
998{,}244{,}353 |
\(998{,}244{,}353\) | カンマが通常記号として働く |
998\,244\,353 |
\(998\,244\,353\) | 微小な空白でもよい |
場合分け
場合分け環境を使います。
HackerRank の場合、数式中の強制改行は \\\\
になります。 \cr
としても同じです。
input
\\[\max(a,b) = \begin{cases} a & (a \ge b \ \text{のとき}) \\\\ b & (a \lt b \ \text{のとき}) \end{cases}\\]
output
\[\max(a,b) = \begin{cases} a & (a \ge b \ \text{のとき}) \cr b & (a \lt b \ \text{のとき}) \end{cases}\]
note
ところで、「の」だけフォントがおかしくなりませんか? MathJax の仕様らしいです。(→ 「の」の謎)
(表示は環境によります)
input | output | note |
---|---|---|
\\((x のとき)\\) |
\((x のとき)\) | おかしい「の」 |
( \\(x\\) のとき) |
( \(x\) のとき) | ふつうの「の」 |
数式中に日本語を置くのは、避けた方がいいかもしれません。
空白はガンガン調整してよい
ここからは 沼 です。
ですが、実はここからが本題です。
空白は、 が自動で良い感じに調整してくれるものだと思っていた時期が、私にもありました…
しかし、そうとは限りません。むしろ空白調整するほうが推奨される場合もあります。
たとえば、カンマが入っている式では、空白を明示的に入れないと解釈が変わってしまいます。
input | output | note |
---|---|---|
a+b, x+y |
\(a+b, x+y\) | (a) と (b,x) と (y) の和 |
a+b,\ x+y |
\(a+b,\ x+y\) | (a+b) と (x+y) |
上記では、\
(バックスラッシュ + スペース)を使って強制的に空白を入れています。
調整するための道具は他にもあります。下記の通りです。
(ここで、em は字幅の単位で、大文字 M の字幅を表します)
( \
だけは実験的に求めた値なのですが、四分アキに相当するものに見えています)
input | output | note |
---|---|---|
MN |
\(MN\) | 並べた文字は積として解釈される |
M\!N |
\(M\!N\) | 負のスペース(-3/18 em) |
M\!\!N |
\(M\!\!N\) | すごい負(-6/18 em) |
M\!\!\!N |
\(M\!\!\!N\) | やばい(-9/18 em) |
M N |
\(M N\) | ホワイトスペースは無視される(くっつけて書くのと同じ) |
M\:\!N |
\(M\:\!N\) | なにこれ?(1/18 em) |
M\;\!N |
\(M\;\!N\) | 意味あるの?(2/18 em) |
M\,N |
\(M\,N\) | 微小なスペース(3/18 em) |
M\:N |
\(M\:N\) | 小さめのスペース(4/18 em) |
M\;N |
\(M\;N\) | やや小さいスペース(5/18 em) |
M\ N |
\(M\ N\) | 普通の空白(4.5/18 em) |
M\enspace N |
\(M\enspace N\) | \quad の半分(9/18 em) |
M\quad N |
\(M\quad N\) | 大きいスペース(18/18 em) |
M\qquad N |
\(M\qquad N\) | かなり大きいスペース(36/18 em) |
M\hspace{12px}N |
\(M\hspace{12px}N\) | 長さ指定スペース |
ええ… 全部いっしょじゃないですか 具体例で見ていきましょう。
…と思いましたが、長くなりそうなので続きは次の機会にします。
興味がある場合は、読み物として 「Takatalab - 正しいマークアップ」をおすすめします。
おわりに
競プロの問題文を打ち込んでいて「なんか読みづらいな」と思うときは、コンテスト参加者にとっても読みづらいものです。
ここら辺、ちょっと意識できるようになると、問題文のクォリティが数段上がります。
私は、大げさな話ではないと思っています。