当サークル代表のp進大好きbot(以下、筆者)がyukicoderでどのように競技プログラミング作問を行っているかを書いていきます。前回の記事は作問時の個bot的な心掛けを書いただけだったので参考になる人はほとんどいなかったと思いますが、作問の仕方は十体十色かと思いますので筆者の方法が合う人もいれば幸いです。
他の人の解説もぜひ参考になさってください。例えばはちじさんの動画が分かりやすいと思います。基本的にはそういった上級者の解説を参考にした方が良いと思いますが、筆者のような下級者でもできる作問方法なら皆さんにもハードルが低く感じられるのではないかと思います。
原案の用意
まず最初に、問題の原案をどのように考えていくかを説明します。
0:問いたいものの言語化
競技プログラミングの問題を作るにあたって重要だと思っていることの1つが、その問題を解くことで競技プログラミングの能力に関する何かが得られることです。例えば
- 入出力や演算子やforループなど、プログラミング言語の持つ基礎的機能を活用する力。
- 単純な1つの数値としては与えられていないデータを数値の組み合わせなどに解釈し直して全探索する力。
- 関数の最小値計算において二分探索を検討し、そのために要件である単調性を検証する力。
- クエリ処理を群演算による代数的操作に翻訳しフェニック木に乗せる力。
- 単純な幾何学的構造を持っていない選択的操作をグラフに翻訳して最短経路問題に帰着する力。
- $1$パラメータの問題群を$2$パラメータ化した拡張問題(いわゆる部分問題)を考えることで動的計画法に乗せる力。
- 関数値の二重和を関数値の線形結合で表示し直して一重和に翻訳する力。
- 明示式を多変数関数の合成と解釈し変数分離や半分全列挙を検討する力。
- 準同型の定義域と終域の演算の計算量の違いに注目して高速化する力。
- 部分集合を選択する最適化問題をフローや畳み込みなどに翻訳する力。
- 実験や検索を駆使して、既知の定理の情報に到達する力。
など、自分なりに言語化した「競技プログラミングの役に立ちそうな力」を直接問いましょう。そうすることで、その問題に挑戦した人たちがその力を新たに得たり、既に持っている力を強化したり、といった恩恵が期待できると考えています。
例えば、自分の好きなアルゴリズムでも定理でも問題でも、何か1つ問う材料を決めてみましょう。そして、その材料を「自分がどう理解しているか」を見つめ直し、その中から1つ以上の「問いたいもの」を決めて見るとよいです。「自分がどう理解しているか」とは例えばアルゴリズムや定理なら、それを活用することで可能になることを自分の中でどう言語化しているか、それにはどんな要件が課されていてそれらはどこで使われているか、どんな罠に気をつけなければならないと思っているか、などです。例えば問題なら、各制約がどういう2つの解法を区別するか、どこを抽象化または特殊化できるか、どう拡張できるか、逆にどういじると全く別物の問題になるか、などです。
ただし、最初から問いたいものがすんなり決まるとは限りません。その場合は、先に次のステップで題材を絞ってから、改めて「その題材であれば何が問えるか」を色々と検討し、その中で特に問いたいと思ったものを選んでみましょう。
1:題材探し
次に、問いたい内容を実際に問うために、問題文を作らなければなりません。そのためには、問題設定を決めなければなりません。もし問いたい内容がシンプルな数式で表せるものであればそのまま問えばいいですし、そうでない場合は問題文の体裁を取るために何らかの題材を用意すると良いです。これは先述したはちじさんの動画でも触れられていますが、題材を用意するためによく言われるのが、身近なものを題材にするという方法です。
例えば農家の方でしたら、農業なり作物なりを題材にするかもしれません。漁師の方でしたら、漁業なりお魚なりを題材にするかもしれません。別に自分の職業や専門性に拘る必要はなく、好きなゲームや物語や人物から連想したストーリーを問題文にしてもよいです。
大事なのは、問題文の意味が明確に伝わるものであることです。問いたいものを問うために問題文を作ったはずが、一意に読解できず問いたいものを問えないのでは本末転倒ですものね。身近な題材を選ぶことがゴールではなく、身近な題材を選ぶことで自分が問題文を推敲しやすくなって一意に読解ができるような問題文を得ることまでがゴールと考えてみましょう。
なお筆者の場合は数学が身近なので、数学を題材にすることが多いです。数学の場合それ自体が競技プログラミングで多用されるため、数学で題材を決めれば競技プログラミングの力として問いたいものが自然についてきやすいため、作問が楽になるのでおすすめです。数学以外でも身近な学問があれば、それを題材にしてみると面白そうですね。他の分野はよく知らないのであまり例は挙げられませんが、化学が身近な人は分子を題材に……分数とか……、生命科学が身近な人は生物を題材に……木とか……ジョルダン細胞とか……、法学が身近な人は法を題材に……貪欲法とか……法$998244353$とか……。
ただし問いたいものを言語化するというステップを忘れてしまうと、競技プログラミングの何の力も問わない問題になりかねないのでそこだけは注意しましょう。例えばプログラミングによる実験も検索による正解探索もなしに答えが求まる数学問題の答えをTextで出力させるだけの問題を出題したくなったら、その問題を解くことで競技プログラミングに関するどのような力が得られるのかを一旦考え直した方が無難かもしれません。
2:原案確定
問いたいものと題材が決まったら、実際にどのような問題設定にするかと、その想定解を(まだ書き下さなくても良いので)確定させましょう。問題を作っても解き方が存在しないのでは仕方ありません。もし想定解が何も思いつかないのであれば「何を問いたいか」がそもそも作問レベルで言語化できていない可能性があるので、そこから考え直すのも良さそうです。その際に、題材は変えても変えなくても大丈夫です。
ただし初めての作問においてはまず完成までのプロセスを一通り経験することも大切なので、もし想定解が思いつかない場合でもその旨を明示した上でtesterさん(作問作業の補助者)を募集し、想定解を代わりに考えていただくというのも手です。
3:公式ページの確認
yukicoder内(wiki含む)のいくつかのページは作問に関係するので予め確認しておきましょう。
- yukicoder Wikiの「作問者・テスター向けページ」
- ヘルプの「問題の出題について」と「レベル・タグについて」
- トップページの「知的財産権について」
などはトラブルを避けるために一読しておくと良いです。合計の文量が多いですが、作問に必要な内容が非常によくまとまっているのでしっかりと読んでおきましょう。
4:難易度設定
問題設定と想定解が確定したら、難易度を評価してみましょう。ただし、難易度を★の数で表す方法は本当に人それぞれですので、決まった評価方法が存在しないという難点があります。
例えば先程挙げたヘルプを参考にし、ヘルプ基準で★2以下に該当しそうであればそのままヘルプ基準でつけてしまっても大きくぶれることはあまりないと思います。
逆に言うとヘルプ基準で★2以下に該当する場合にヘルプ基準と異なる難易度設定した場合、難易度の過少申告または過大申告になりやすく、その場合はまたそもそもヘルプを読んでいないと推測されるので、その他の最低限の作問作業、例えば上述したyukicoder Wikiの作問者・テスター向けページを読んだり想定解の正しさを確認したりなどすら行っていないことを疑われるリスクもあると思います。
特に過少申告は、特定の難易度帯を抜本的に解いていくタイプの学習をしている人達に不必要な学習ペースダウンを強いてしまう危険性があるので注意しましょう。例えば★1で64bit整数を問いたり、★1.5で基本アルゴリズムを非自明な形で問いたり、★2で初等的でないデータ構造を非自明な考察とセットで問いたり、などがありがちです。ありがちということは前例があるということなのでそれを根拠に難易度設定できなくもないですが、その際には難易度投票結果も参考にしましょう。
またヘルプ基準で★2.5以上に該当しそうである場合は、そのままヘルプ基準でつけても構いませんがコンテスト参加者から難易度の過大申告とみなされる可能性もあります。何故なら、難易度設定の傾向は年々変化しているようで、ヘルプが作成された当初よりも「同一難易度」に難しめの問題を置く傾向があるからです。
これは恐らく難易度という用語の曖昧さからくる問題で、競技プログラマーはAtCoderのdifficulty(コンテスト参加者を母集団としてどのくらいの実力者にどのくらい解かれたか、という母集団依存の難易度)を連想しているために競技プログラマー全体の水準が上がれば上がるほど「同一難易度」に難しい問題を分類していくことになっているのだと推測しています。
ヘルプに従っている以上は不備とまでは言えませんので、過大申告とみなされても構わない場合(特に単独コンテストで一貫した難易度評価をしている場合)はそのままで大丈夫だと思います。仮に過大申告とみなされても、過少申告と違って特定の難易度帯を抜本的に解いていくタイプの学習をしている人達に不必要な学習ペースダウンを強いてしまう危険性がない点が安心です。
一方でもし過大申告とみなされたくない場合は、追加でyukicoderの過去問及びそれらへの難易度投票結果と照らし合わせて難易度を決めていきましょう。ただし前述した通り、年々「同一難易度」に置かれる問題が難しくなっていっているので、過去問を参考にするならばあまり古いものは除いた方が良さそうです。
どこまで除くかは明確に決められませんが、筆者は1年を境に切っています。理由は、yukicoderで1年毎にAdvent Calender Contest(通称アドベコン)という最も大きなイベントが開催されるため、その前後でyukicoder利用者全体の水準が急激に上昇すると推定しているからです。というのも、問題数自体は1ヶ月分のコンテストに及ばない一方で難易度の高い問題が比較的多めに出題されるため、★2.5以上を解く参加者ばかり鍛えられると考えているためです。
いずれにしても難易度を決める際は、その根拠を自分の中で明確に、主観的で構わないので言語化しておくことをおすすめします。でないと自分でも再現性のない何となくの難易度評価となってしまい、それが妥当であるか否かにあまり期待ができないからです。
ヘルプを基準にしたならそれでも良いですし、過去問数ヶ月分を基準にしたならばそれでも良いですし、過去問数年分を参考にしたならそれでも良いです。大切なのは、難易度評価を適当ではなく自分の中で納得行く形で与える程度の誠実さを持つことです。
例えばヘルプとも過去問とも整合的でない難易度評価をしてしまった時、その理由を明確に説明するwriterさんと「すみません、深く考えずに★1をつけました」と言ってしまうwriterさんとではコンテスト参加者が感じる印象は異なり、後者ではwriterさんの素直さは評価されてもwriterとしての信用が損なわれることになると思います。説明する機会がなかったとしても、あまりにも奇抜な難易度評価をしてしまったらそもそもヘルプも過去問も何も参考にせずに準備したことを疑われるかもしれず、後者とあまり印象は変わらないかもしれません。そういう事態を避けるためにも、きちんと難易度評価を自分の中で言語化し、納得行くまで調整しましょう。
また難易度評価を自分の中で言語化しておけば、コンテスト参加者の解け具合や投票結果との間にズレが出た時に、どういう考察や実装が競技プログラマー全体の中でどの程度簡単か難しいかなどを把握しやすく、それによって自分自身の強みや弱みを把握しやすいという利点があります。
ちなみに自分で作問する際の話とは関係ないですが、上級者のwriterさんが開いたコンテストで難易度評価が過小申告方向にズレてしまった時に難易度評価の説明をtwitterで呟いてくれることがよくあります。そういう時も、上級者の方がどういう視点で考察をしてどういう連想を当たり前と思っているかが分かるので、自分が上達するために身につけるべき視点を知ることができるのでありがたいですね。
執筆作業
問いたいものと題材と難易度が決まったら、早速それらを元に問題ページと解説ページを編集しましょう。
5:問題ページ編集
問題ページには問題文、入出力形式、入力制約、サンプルなどを書きましょう。そしてそれらにじっくり目を通し、第三者が一意に読解できるか否かを確認しましょう。一意に読解できればそれ以上の工夫はオプションだと思って良いと思います。そのくらい、一意に読解できることは重要です。今一度、
- 複数通りの解釈ができる言い回し(主語の省略による曖昧な言明など)をしていないか。
- 複数通りの流儀がある用語(自然数、グラフなど)を曖昧な形で使用していないか。
- 課している条件(入力で与える数値の上限など)を全て明記しているか。
- 変数が何の要素であるか(整数であるか、整数とは限らない実数であるか、など)を明確にしているか。
- 未定義語(明示的に定義されていない記号など)を用いていないか。
- 背景に特定の題材がある場合、その題材を熟知している人にだけ備わる知識(登場人物同士の関係や専門用語など)を暗に仮定していないか。
などを確認してみましょう。
それ以外の工夫としては、複数通りの正しい書き方がある場合にどちらを採用するかを、競技プログラマーが読み慣れている書き方に合わせることで読みやすくする、などがあります。例えば
- インライン数式環境の直前(resp. 直後)が行頭(resp. 行末)でも句読点でもカッコでもない場合に半角空白を入れる。(数式と地の文がはっきり分かれて少し読みやすくなります。ただし、長い数式であればインライン数式環境にはしないでディスプレイ数式環境にした方が読みやすく感じる人が多そうです)
- 古い問題でよく使われていた”YES”や”NO”を使わず最近の問題でよく使われている”Yes”や”No”を使う。(そこを気にするならそもそもコンテスト概要ページに”Yes”と”No”を使う旨を明記する手もあります)
- 注釈なしでは曖昧さがあるが1つの意味のみ頻出であるため競技プログラマーならば難なく意味を推測できる語は、クリックして初めて開く場所に定義を秘匿する。(ただしこれは慣習を知っている競技プログラマーを優遇しており、逆に言えば初めて競技プログラミングに挑戦する新規参入者にのみ追加で読解時間を課すことになるという欠点もありますし、クリックして開かなければ曖昧なままという見方もあります)
- 改行区切りでの出力を要求するスペシャルジャッジ問題で半角空白区切りも許容する。(ただしこれは厳密には誤解法を通していることになる上に、そういうスペシャルジャッジ文化を知らない人、特に新規参入者に不利な状況を生んでしまうという欠点もあります)
といった配慮があります。この他にも、実行時間制限やメモリ制限が厳しい場合に注意を書いたり、見落としやすそうな制約や入出力形式を強調したり、サンプルには入出力の説明だけでなく出力の理由も詳しく説明したり、といったヒントに類する工夫もあります。どのような工夫をしていくかはwriterさんの思想にもよるところなので、自由に決めていきましょう。
ちなみに問題ページでは問題の種類を「通常問題」や「ネタ問題」などから選べます。実はタグ検索などでデフォルトで表示されるのは通常問題だけなので、特別な事情がない限り通常問題にしてリーチしやすくしておくことをおすすめします。また通常問題以外を選択した場合に批それが妥当でないと判断されれば批判を浴びることもありますので、他を選択するのであれば少なくとも自分の中ではその理由を言語化しておくと良いです(例えばネタ問題であれば「エイプリルフールコンで出題するための問題なので」など)。
6:解説ページ編集
解説ページにはTLE解法や、想定解法とその正当性などを書きます。更に実行時間制限に気をつける必要がある問題では時間計算量の上界の評価を、メモリ制限に気をつける必要がある問題では空間計算量の上界の評価を書くことが望ましいです。TLE解法は必須ではないですが、そもそもTLE解法も思いつかない人も解説を読む可能性があるので書くと親切ですし、その計算量を書くことで入力制約が妥当なものであるかを自分でも確認しやすくなるという利点があります。
もしオマージュ元があればそのリンク、一次ソースがある内容は出典を書きましょう。問題ページには問題のヒントとなり得るリンクを載せにくいですが、解説ページはその限りではないのでcreditすべき対象をきちんとcreditすることが望ましいです。
問題文と違って高速な読解が可能である必要はないので、じっくり厳密に読んでもらうことを想定した解説を書いてもよいです。しかしあまりに込み入った議論を書いてしまうとほとんど誰にも読めない解説になってしまうかもしれません。
大切なのは、想定読者を意識することです。★いくつまでどの程度解ける人を対象にしているのかを最初に決め、それに合わせた粒度で議論を書いていくとよいです。基本的には問題の難易度より★が少ない問題しか解けない人も勉強のためにupsolveを目的として解説を読む可能性があるので、そういった人もなるべく対象として想定することがおすすめです。
どのような解説が良いかはwriterさんそれぞれに意見があると思いますが、筆者は想定読者が頑張れば読めて勉強になる内容をなるべく丁寧に書くのが解説の良いあり方だろうと思っています。ただし「頑張れば読めて」というのはアルゴリズムや考察部分を理解するための頑張りに関する言明であって、説明の不十分さや曖昧さ、議論の飛躍や不正確さに起因する頑張りは意図していません。そういった不必要な頑張りを排除した上で読者が全力で解説から学びを得ることを実現するためにも、説明は十分かつ明快に、議論は飛ばさず正確に書いていくように心掛けましょう。
ここで、正確に書くということは何もふわっとした説明を書いてはならないということではないことに注意しましょう。上で述べたのはあくまで、不正確な記述に起因する頑張りを読者に要求すべきではないという話です。例えばそれ単体で正確な記述に加えてふわっとした補足で理解の助けをすること自体は(ふわつとした補足が正確な記述と正面衝突して矛盾しているなどではない限り)問題ではありません。読者が厳密に理解できる助けとなることであれば、色々な説明方法を検討してみるのが良いですね。
また、書き終えた解説を参考にタグをいくつかつけてみましょう。そうすることで、コンテストに参加しなかった人にもリーチしやすくなりまいます。タグをつけない人も少なくはないのですが、タグ付けは大した手間も掛けずに他の人達の役に立つので、是非積極的につけることをおすすめします。どのようにタグを付けるべきか迷うかもしれませんが、例えば「DP」と「動的計画法」のどちらにしようかを考える際は
でどちらが人気かを調べてみるのが良いと思います。
テストケースページの編集
7:テスト入力の登録
テストケースには
- サンプルケース
- 小さいケース(特にコーナーになりえるもの)
- 大きいケース(想定しているTLE解法でもぎりぎり通るものもいくらか)
- 最大ケース(ランダムケースや、上限ギリギリのケースや、想定しているTLE解法にとっての最悪ケースなど)
を入れておくと良いでしょう。TLE解法が通るケースがサンプルケースと小さいケースだけだと、想定解が間違っている時に気付きにくいので注意しましょう。最大ケースには実際に上限ギリギリであるものを追加しないと、全てのテストケースで正解できる提出が制約範囲内の全ての入力に対して正解できるかの期待が弱まってしまいます。
もし様々な嘘貪欲や嘘乱択が想定されるなどの事情でテストケースの強さが不安であれば、マルチテストケースにしてしまいましょう。通常は$2$桁個くらいしかないテストケース数を、合計$10^6$個くらいにできるので非常に強固になります。
テストケースの作成から登録までの作業はそこそこ面倒なので、今後も作問していくならばある程度自動化するように環境を整えておくと良いです。特に、ファイルを手作業で新規作成してそこにテストケースをコピーして名前をつけて保存、という作業はかなり時間がかかるのでフォルダを自動で探索してそこにファイルを自動生成してそこにテストケースを直接ファイルに書き込むような仕組みを実装しておくのがおすすめです。
またテストケース生成を自動化しておくことで、assertなどを用いることができるので問題ページで指定した入力制約や入力形式と整合的であるように作成しやすいです。テストケースページに登録後のテストケースを手作業でチェックするのは大変な上にミスしやすいので、なるべく計算機にお任せしましょう。
テストケースの個数は、
- マルチテストケースでないならば$20$個以上
- ジャッジが非常に軽いのでなければ$50$個以下
を目安にすると、極端に弱いテストケースを避けつつ極端にジャッジ負荷を掛けてしまうことを避けやすいです。
ちなみに入力の個数が$10^5$オーダー以上のファイルは1つでも、低速な回線だとyukicoderにアップロードするのに分単位で時間が掛かり、最悪の場合散々待った挙げ句にエラーメッセージが出て実質のTLEとなります。高速な回線を使うか、yukicoder上のテストケース作成機能を使うようにしましょう。
8:想定解の登録
テスト出力を登録する前に、想定解の提出までしてしまうのがおすすめです。別にそうしなくてもよいのですが、少しメリットがあります。それについては後述します。
なお、この際に想定解には入力制約を確認するassertなどを入れておくと安心です。テストケース生成時にもassertをしている場合は不要ですが、writer解にassertが入っていればtesterさんやコンテスト後のユーザーにも確認することもできるので作問作業への信頼を与えられますし、またバリデーション機能を使わずに済むのでtesterさんが望むならそこを自由に使っていただけるという利点があります。
想定解を提出すると、この時点でテスト出力が未登録であればACではなくNoOutというジャッジステータスになります。提出した想定解に「想定解指定」ボタンが表示されますので、これを押下すると解説ページ末尾に自動でその提出へのリンクが貼られます。リンク名はデフォルトでは「想定解法(言語名)」ですが、「想定解法」の部分は手動で指定し直すこともできます。例えば解説ページで解法が複数解説されていれば「解法1」や「解法2」、「writer解」と「tester解」といったリンク名にしてしまうのが分かりやすいですね。ちなみに表示されるリンクの順番は辞書順のようです。
9:ジャッジコードの登録
スペシャルジャッジ問題やリアクティブ問題においては、ジャッジコードページにジャッジコードを登録します。この際、ジャッジコードは高速であればあるほどコンテスト参加者が快適にコンテストを楽しめるので、なるべくジャッジコードの高速化に努めましょう。
例えばテスト入力の制約を確認する処理は、単純なassertではない限りジャッジコードに含めない(最初動作確認で含めたとしても最終的には消すかコメントアウトする)方が望ましいです。でないと提出があるたびにその処理が走ってしまい、ジャッジに無駄な負荷がかかってしまいます。assertは優秀なコンパイラを持つ言語であれば最適化に寄与する可能性があるので、コンパイラと意思疎通ができる人は各自の判断で残しても良いと思います。
また、ジャッジコードはある程度可読性の高いものにしましょう。でないと、testerさんがジャッジの挙動を確認する際に困ってしまいます。というわけで、ジャッジコードは高速でかつ優れたコンパイラを持ちかつ可読性の高い言語……そう、C++……で書くことをおすすめします。
10:テスト出力の登録
実はテスト入力さえ登録しておけば、テスト出力はいちいちファイル生成してアップロードせずともyukicoder上で高速に生成することが可能です。
具体的には、テストケースページ下部にある「(提出番号)を想定解として出力ケースを生成」というボタンを押下することで、その提出番号を持つ提出が実行されてその出力がテスト出力に自動で登録できます。提出番号のデフォルトは、想定解指定されているものがあればその提出番号となります。これがテスト出力登録より前に想定解を登録するメリットですね。
なお、想定解が間違っている場合はそのまま間違った出力が登録されることに注意しましょう。そのような事態を検出できるように、想定解だけでなく想定TLE解も提出しておき、それがTLE以外のステータスはACとなっていることを念の為確認することがおすすめです。またこの作業で想定解のミスを見つけやすくするためにも、TLE解法が通るテスト入力もそこそこ多めに登録しておくと安心です(もしそのようなケースが不要に感じたら、最終的に削除すればよいです)。
もし想定解のミスが不安であれば、上記の方法でテスト出力を登録するのではなくTLE解を用いて手元でテスト出力ファイルを生成しておき、それをアップロードするようにしましょう。時間はかかりますが、TLE解法が愚直であればテスト出力の信頼性が増します。
またテストケース生成機能は改行の扱い周りでたびたびアップデートが入っているので、自分の想定した挙動でないかもしれません。yukicoder運営さんのアップデート情報はこまめに確認し、またテスト入出力がサンプルと整合的であることを確認しましょう。
11:実行時間とメモリ使用量の確認
writer解がぎりぎり通る実行時間制限とメモリ使用制限にしてしまうと、想定解であってもTLEやMLEになってしまうかもしれません。先程挙げたyukicoder Wikiの「作問者・テスター向けページ」の作問から公開までの流れにも明記されているように想定解を定数倍悪くしたぐらいでも通るようにしましょう。
実際にwriter解の実行時間と比べてどの程度にすればよいかはwriter解をどの言語で提出したかに依存してしまいますが、例えばC++ならば最低でも$4$倍、できれば$8$倍、C++特有の高速化が効いてしまっているならば数十倍の時間を見込んだほうが良いかと思います。普段ご自身がコンテストに参加する際に実行時間制限からどのくらいの余裕を持ってACとなっているかをご確認の上、それに合わせて実行時間制限を決めていきましょう。
また実行時間制限やメモリ制限は小さければ小さいほど良いというわけではないので、ぎりぎりを攻める必要もありません。デフォルトの2000[ms]と512[MB]で問題なく想定解と想定TLE解法が区別されるのであれば、基本的にはそのままで大丈夫です。
もし実行時間制限を短くしても入力制約の上限を大きくしてもどちらでもうまくいく場合にどちらを変更しようか迷った際は、元の実行時間制限が2000[ms]だったら入力制約の上限を大きくし、実行時間制限が2000[ms]以上であれば実行時間制限を短くするのがおすすめです。実行時間制限がデフォルト値のままの方が考えやすいですし、実行時間が短いほうがジャッジの負荷も小さく、またオンライン実行が実行時間制限超過となりにくいので実験もしやすいという利点があります。
最終チェック
ここまでで作問作業の大部分は終了です。後もう少しですので、最後まで気を抜かずに頑張りましょう。
12:作問チェックリストの再確認
先程挙げたyukicoder Wikiの「作問者・テスター向けページ」の作問用チェックリストを再度確認しましょう。
何か明確な理由があってチェックリストに従っていないのであればそれはwriterさんの判断ということになり尊重されると思います。一方でチェックリストをそもそも確認すらしていない場合、チェックリストに反しているのであれば信用を落とすことに繋がりますし、たまたまチェックリストと整合的であっていたとしてもそれは結果論であってあまり誠実な態度とはみなされないと思います。
例えばチェックリストで名指しされている多義語である「グラフ」をあえて注釈なく用いていれば、まず意図的にそのようにする合理的理由がないと判断され、チェックリストを読んですらいないのだろうなと推定され信用を落とすことになると思います。
せっかく分かりやすいチェックリストが用意されているのですから、面倒がらずに利用して誠実な作問作業を心掛けましょう。
13:testerの募集
もう自分では不備が見つけられなさそうだ、という状況になったらtesterさんを募集しましょう。twitterやyukicoder slackで募集の旨を投稿することで、それをたまたま目にした人がtesterさんを引き受けてくれるかもしれません。その際には想定難易度と、もしあるならばコンテスト開催予定時期も明記しましょう。そうでないと最悪ケース(★5以上で翌日開催)が想定されてしまい、よほど高速なtesterさんでないと応募してくださらないかもしれません。
それでもtesterさんが見つからない場合や、コンテスト開催予定時期が短くて長々と募集をかける余裕がない場合は、各種DMで知り合いに直接testerをご依頼する手があります。もちろん筆者にご依頼くださっても構いません。★2.5以下なら基本お引き受けしますし、★3以上も要相談ですがお引き受けするかもしれません。
testerさんは基本的には1人いれば問題ありませんが、testerさん自身が追加要員を要求した場合や、writerさんが複数からのご意見をいただきたい場合は2人つけるのも良いと思います。人数が多い分だけ作問時のやり取りが煩雑にはなりますので、2人くらいに留めておくのがおすすめです。
なおデータのない主観的な体感としては、3人以上のtesterさんがいる問題は問題文の不備や解説の飛躍や難易度評価のズレ(ヘルプとも過去問とも整合的でない評価)が生じやすい傾向にあると考えています。上述したようにやり取りが煩雑になることに加え、群集心理や楽観バイアスがかかって「とても信頼できる他の人が何も言ってないってことはきっとヨシ!」が起こりやすいのかもしれません。
とはいえこの辺は未経験なため、全然あてにならない推測です。3人以上いてもお互いに遠慮なく意見をバンバン出せる間柄であればあまり問題ないかもしれません。
ちなみに最近は、twitterで初めてDMを送る相手にurlを含めて送るとアカウントロックの危険性があります。testerさんにテスターコードを送る際に注意しましょう。
14:testerのフィードバックへの対応
testerさんのフィードバックを受けて、修正したい箇所があれば修正しましょう。この際に気をつける必要があることは、入力制約かテストケースを変更した際に新たな入力制約とテストケースが整合的であることの確認し忘れることです。
- 新たにassert付きの提出をするかバリデーション機能を使うことで再確認する。
- スペシャルジャッジ問題やリアクティブ問題では、ジャッジコードも再確認する。
- 問題ページの編集ページで「テストファイルを作成する」ボタンを一度でも押下していると問題ページ保存のたびにサンプルがテストケースに追加されるので、問題ページ保存後のテストケースにも注意する。(これはそもそもサンプルが正しければあまり問題にはなりませんが、サンプルに余計な空白や改行がある場合に問題が起こり得ます)
フィードバックを受けての変更点は基本的にtesterさんとも共有しましょう。でないと、変更点部分に誤植などがあったりした時にtesterさんが気付けず、そのまま残ってしまう危険性があります。
こうしてフィードバックを繰り返し、testerさんからもそれ以上の変更が不要そうな旨を確認できれば、問題ページの編集ページから「作業中(WIP)」のプルダウンメニューを「問題完成」に変更し、testerさんにその旨を報告しましょう。そうすることで、問題タイトルに自動で付加される「(WIP)」の文字列が削除されます。
逆に言うと、このプロセスを経ていない問題はタイトルに「(WIP)」が残ってしまい、コンテスト開始時に参加者の目に触れてびっくりされます。たまにしか起こりませんが、気をつけていきましょう。
15:出題申請
こうして完成させた問題は、twitterのDMでyukicoder運営さんに出題したい旨を連絡しましょう。その際には以下の情報も明記しましょう。
- 1問だけの単発出題をしたいか、コンテストで出題したいか。
- その問題のurl(コンテストで複数問出題したい場合も、後でコンテストページから追加できるので1問だけで良いです。ただしオムニバスコンを除く)。
- いつ出題したいかの希望(なければなしでよいです)
ただしtwitterのDMはちょくちょく不具合があって通知されなかったりするので、1日経ってご返信がなければDMした旨をリプライでご連絡すると確実です。また先述したように最近は、初めてDMを送る相手にurlを含むDMを送るとアカウントロックの危険性がありますので注意しましょう。
コンテスト開催へ
運営さんへの申請が通ったら、後はコンテスト開始までドキドキするのがwriterの主な仕事です。あまり遠い日程でコンテストを組むと長期的にドキドキする必要があることに注意しましょう(yukicoderのコンテスト予定には波があり、翌週に開催できる週もあれば、2~3ヶ月待ちとなることもあります)。
16:コンテスト概要の編集
コンテストの開催が決まれば、コンテスト概要の編集が可能になります(ただしオムニバスコンを除く)。そのコンテスト特有の注意事項や過去の関連コンテストの紹介がある場合はコンテスト概要に記載しておきましょう。
ただし問題を解くために役立つ重要な情報は問題ページにも記載をしておくことをおすすめします。そうでないと、コンテスト概要を読み飛ばしてしまったコンテスト参加者や、コンテスト後に問題一覧やタグ検索などから問題ページに飛んで練習をしているユーザーは、その記載に気付きにくいからです。ただしこれに関しては気にしていないwriterさんが多そうな気がするので、そこまで強く気にする必要はないかもしれません。
例えば何らかの外部イベントのためにコンテストを開く場合、そのイベントで説明される内容(問題を解くために必要なキーワードや、イベントオリジナルの難易度設定など)は問題ページにも記載した方が親切かなと思います。もしキーワードが不足していればそれは問題を解くための情報が減ることになりますし、もし難易度設定がオリジナルのものであれば特定の難易度帯を解いて練習しているユーザーの学習の妨げになるかもしれません。
とはいえそこまで神経質になることが求められているとは思いませんし、色々なwriterさんが自由に活動できることも大事です。この辺は自分の中で理由を言語化しておくことだけ気を付けて、writerさん個々人の考えを大切にしてくださればと思います。
17:コンテストの告知
twitterなどでコンテストの宣伝をしてみましょう。コンテスト予定自体はyukicoderのコンテスト一覧からも見れるので宣伝しないとコンテストに気付かないということはあまりないと思いますが、日々様々なコンテストに参加している競技プログラマーにとってリマインダの機会があること自体は良いことだと思います。
またその際に全体testerを募集する人も多いです。1問1問のtesterさんと違い、完成したセット全体なり特定の問題群なりを試走していただき、問題が難易度順に並んでいるかや難易度勾配が急すぎないかを判定してもらうのが主な役割です。
もちろんその際に何か不備に気付いた場合はフィードバックをしてもらえるかもしれませんが、全体testerさんを募集する段階では個別testerさんのフィードバックを受けてWIPを外しているので、あまりそういった事態は起こらないことを期待しましょう。(ただし「ヨシ!」を起こさないためにも、そういったフィードバックを遠慮なく行えるような関係性を持つことが望ましいですね)
18:開催記の執筆
せっかくコンテストを開くなら、その準備中のあれこれや各問題のあれこれやコンテスト本番のあれこれを記事にするのも楽しいです。ただしコンテスト後に執筆を始めると、記事の公開がコンテストよりだいぶ後になってしまったりで想定読者があまりコンテストのことを覚えていなくなってしまうかもしれません。
そこで、準備中のあれこれや各問題のあれこれなどコンテスト前に書けるものはある程度書いておくのがおすすめです。ドキドキ期間にドキドキするだけよりもいくらか気も紛れて良いかもしれませんし。
19:コンテスト本番
コンテスト開始より15分前までに、writer名義のアカウントでyukicoderのいずれかのページを開きましょう。万が一10分前までに開いていなければ延期となる方針が公開されています。
これはwriterさんがコンテスト不在となる事故を防ぐための措置です。writerさんがコンテスト不在となると、コンテスト参加者からのclar(質問機能を用いた、writerへの質問)があってもwriterさんが対応できず、もし問題に不備があってもそのままとなってしまうという危険性があります。
というわけでコンテスト不在を避けた上で、clarがあれば対応していきましょう。コンテスト中に答えたくないclar(例えばコンテスト中に答える必要性が薄く、時間をかけて回答したいclar)があれば「コンテスト後に回答します」などと返答することも可能です。
またclarの内容及びその返答は、デフォルトでは非公開(質問者本人とwriterとtesterと運営のみ閲覧できる情報)です。例えば問題文の不備の指摘があって問題文を修正する時などコンテスト参加者全体に告知したい場合は、回答時に全体への公開を示すチェックボックスにマークをしてから送信しましょう。
20:コンテスト後
コンテスト終了直後には参加者にお礼を述べ、適宜コメントをする人が多いです。また開催記があれば、それも公開しましょう。
なおコンテスト後にclarがあることもあります。コンテスト後のclarは一切対応しない(というか多分コンテスト後にyukicoderを年単位で開かなくなってしまいclarが来ていることに気付いていない)人も多いですが、できる限りclarは対応した方が(コンテスト後とはいえ練習として問題を解く人もいるので、問題の不備などが残っていることは好ましくないでしょうから)望ましいと思います。
コンテストでは想定外のことも起き、色々な反省もあるかもしれません。それでも全体的には楽しく終わることが大半なのではないかと期待します。反省も楽しさもどちらも大切にして、次のコンテストの準備につなげていきましょう。
長くなりましたが、以上です。お読みいただきありがとうございます。