atcoderをrustで解くためのリポジトリ
ユーザー名:shonNMIXX
https://atcoder.jp/users/shonNMIXX
.envファイルを作成する。webサイトのデベロッパーツールからセッションIDを取得後に
REVEAL_SESSIONの値に追加する
REVEAL_SESSION="<session_id>"その後にsetup.shを実行する
./setup.sh
./compete-new.sh abc<hoge>各コンテストのディレクトリ内で下記のコマンドを実行する
cargo compete test <problem>各コンテストのディレクトリ内で下記のコマンドを実行する
cargp compete submit <probelm>解答ファイル内の solve と愚直解 naive に同じランダム入力(Python 生成)を渡し、
cargo test で出力一致を検証する。
src/<contest>/stress/(naive_test.rs=貼り付け用雛形 / gen.py=ジェネレータ雛形)は
./compete-new.sh 実行時に snippets/stress/ の雛形から自動生成される
(既存ディレクトリでも足りないファイルだけ補充される)。
- 解答ファイルの
mainの中身をfn solve(input_str: &str) -> Stringに切り出し、mainを stdin 読み込みとprintln!("{}", solve(&buf))の 3 行にする src/<contest>/stress/naive_test.rsの内容を解答ファイル<problem>.rsの末尾に貼り付けるPROBLEM定数を問題名に合わせ、naiveを実装する(遅くてよいので確実に正しい解法)gen.pyをgen_<problem>.pyにコピーして制約に合わせて実装する
以下は各ステップの詳細(例: src/abc999 の a.rs、N と長さ N の配列 a を受け取り、
総和を出力する問題)。
./compete-new.sh 直後の a.rs は次の形(snippets/snippet.rs そのまま)。
#![allow(unused_imports, dead_code)]
use itertools::Itertools;
use proconio::input;
use proconio::marker::{Chars, Usize1};
use std::cmp::Reverse;
use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque};
fn main() {
input! {
n: usize,
a: [usize; n],
}
}input! の中身を書きながら解答を完成させたら、main の本体を
solve(input_str: &str) -> String へ丸ごと移し、main を stdin 読み込みと
出力の 3 行だけにする。
// 入力文字列をパースして答えの文字列を返す。
fn solve(input_str: &str) -> String {
let mut source = proconio::source::once::OnceSource::from(input_str);
input! {
from &mut source,
n: usize,
a: [usize; n],
}
a.iter().sum::<usize>().to_string()
}
fn main() {
let mut buf = String::new();
std::io::Read::read_to_string(&mut std::io::stdin(), &mut buf).unwrap();
println!("{}", solve(&buf));
}複数行を出力する問題は Vec<String> に貯めて .join("\n") で 1 つの
String にまとめて返す。
cat src/abc999/stress/naive_test.rs >> src/abc999/src/bin/a.rs貼り付けると a.rs の末尾に #[cfg(test)] mod random_tests { ... } が
追加される。このブロックは提出ビルドには含まれないので、貼り付けたまま
提出してよい。
問題が a なら、貼り付けたブロック内の定数はそのままでよい。
b, c ... の場合は書き換える。
const PROBLEM: &str = "a";この値が gen_<PROBLEM>.py / ng_<PROBLEM>.txt のファイル名解決に使われる。
todo!() になっている naive を、遅くてよいので確実に正しいロジックで
埋める。solve の結果には依存させず、入力を独立に読み直すこと(solve
にバグがあっても naive 側では顕在化しないようにするため)。
// 愚直解。solve の結果に依存させず入力を独立に読み直し、正しさ優先で実装する。
fn naive(input_str: &str) -> String {
let mut source = OnceSource::from(input_str);
input! {
from &mut source,
n: usize,
a: [usize; n],
}
let mut sum = 0usize;
for x in &a {
sum += x;
}
sum.to_string()
}cp src/abc999/stress/gen.py src/abc999/stress/gen_a.pygen_a.py を問題の制約・入出力形式に合わせて書き換える。argv[1] が
seed として渡されるので、random.seed(seed) で再現性を持たせる。反例を
見つけやすいよう N 等は小さめ(1〜8 程度)にするのが基本。
import random
import sys
def main() -> None:
seed = int(sys.argv[1])
random.seed(seed)
n = random.randint(1, 8)
values = [random.randint(1, 20) for _ in range(n)]
print(n)
print(*values)
if __name__ == "__main__":
main()各コンテストのディレクトリ(src/<contest>)内で下記のコマンドを実行する。
cargo test --bin <contest>-<problem>全 seed(0..TRIALS)で solve と naive の出力が一致すれば成功する。
test random_tests::stress ... ok
不一致が出ると seed・入力・両出力を panic メッセージに表示し、入力を
stress/ng_<problem>.txt に保存して失敗する。反例は下記でデバッグできる。
cargo run --bin <contest>-<problem> < stress/ng_<problem>.txt例(abc461 C、src/abc461 内で実行):
cargo test --bin abc461-c
cargo run --bin abc461-c < stress/ng_c.txt # 反例のデバッグ- 貼り付けたテストブロックは全体が
#[cfg(test)]なので提出ビルドに含まれず、そのまま提出してよい。 - seed は
0..TRIALSの連番で、毎回同じ入力列になり再現性がある。 gen_<problem>.pyが未作成の場合はその旨のメッセージでテストが失敗する。- 比較は出力文字列の完全一致(前後 trim のみ)。答えが一意でない問題はテスト内の比較部分を 問題に合わせて書き換える。インタラクティブ問題は対象外。
- 旧方式(
stress.py+<problem>_naive.rsの別バイナリ比較)はレガシーであり、 過去コンテストのディレクトリに残っているものは履歴として保持するのみで新規には使わない。