blogskol

思わず声に出して読みたくなるブログ

競プロの環境

最適解ではありません
知見はいつでも募集中
いろんな人のコードを見よう

oj

oj を入れておく
以下の内容のいくつかは oj と重複している

エディタ

一番よく聞くので vscode

template

template-dir にデフォルトで欲しいファイルを全て置いておく
問題を解く時はこのディレクトリの中身を丸々コピーする(後述)

b.cpp

メインのコード
#include <bits/stdc++.h>REP マクロなど欲しいもの全部書いておく
pragma region を用いることでエディタ上ではこれらのテンプレートは全て隠して置ける

tle.cpp

愚直解用のコード
b.cpp と同じ準備をしておく

make_random.py

入力生成をするコード
便利な関数を書いておく
INTS(l,r,n) : 各要素が l 以上 r 以下の、長さ n の配列を返す
木やグラフ、distinct な数列などを作る関数なども書いておくと良い 関数は全てさらに別の library.py に書いておけば import を書くだけで良くなる

cnt.txt

自分で新たに作ったサンプルの個数を数えるようのファイル
0 を書いておく

.bashrc

alias や関数を書いておくことで、ターミナルで色々実行できるようにする
PATH と書いてある部分は実際には絶対パスが入る

基本
alias g++='g++-11 -std=gnu++17 -D=__LOCAL -O2 -I PATH/acl -I PATH/local'

alias G='g++ b.cpp'
alias GTLE='g++ tle.cpp -o tle.out'

alias a='./a.out'

alias TLE='code tle.cpp'
alias RA='code make_random.py'

alias ..='cd ../'
mkcd(){
  mkdir $1
  cd $1
}

g++-D=__LOCAL__LOCAL を define させてる理由は後述

新しい問題を解く時
np(){
  mkcd $1
  cp -r PATH/template-dir/ ./
  chrome_cpy
  oj d "$(pbpaste)"
  code b.cpp
}

例えば np a と打つと

  1. ディレクトa を作成しそこに移動
  2. atemplate-dir の中身がコピーされる
  3. chrome_cpyapplescript を用いて現在 Google Chrome で開いているページのリンクをクリップボードにコピーする alias (中身省略)
  4. oj d "$(pbpaste)" によってそのリンクのサンプルをダウンロードする
  5. b.cpp を開く
愚直解との比較
C(){
  for _ in $(seq $1);do
    python make_random.py > input.txt
    ./a.out < input.txt > wrong.txt
    ./tle.out < input.txt > correct.txt
    if [ "$(diff --brief wrong.txt correct.txt)" = "Files wrong.txt and correct.txt differ" ];then
      echo "$(<input.txt)"
      echo "Wrong:"
      echo "$(<wrong.txt)"
      echo "Correct:"
      echo "$(<correct.txt)"
      cnt=$(cat cnt.txt)
      cnt=$(( $cnt + 1))
      echo "This sample is x-sample${cnt}"
      mv input.txt test/x-sample${cnt}.in
      mv correct.txt test/x-sample${cnt}.out
      echo $cnt > cnt.txt
      break
    fi
  done
}

ランダムケースを生成し、./a.out./tle.out の結果を比較
結果が異なっていた場合、入力と二つの結果を出力したのち、そのケースを test/x-sample1 として追加する(添字は毎回増える)
従って、以降 oj t をした場合既存の sample に加えて新しい hack ケースもチェックしてくれる

以上のことを C n で hack ケースが見つかるまで実行する
ただし n は実行上限回数で、hack ケースが見つからないまま n 回試し終わった場合そこで止まる

提出
TS(){
  out=false
  for f in test/*.in;do
    result=$(diff --brief <(./a.out < $f) ${f/.in/.out})
    if [ ${#result} -ne 0 ];then
      out=true
      break
    fi
  done
  if "${out}";then
    oj t
  else
    oj s b.cpp --yes
  fi
}

提出用の関数
test 内全て一致するか試し、撃墜ケースがあれば出力、無ければ提出をする
後でコンパイル機能付きのも作る

debug

local/debugに以下を書いておく
別に b.cpp に直接書いても良いが、local でしか実行しない関数なので #include した方がかっこいいと思う

#  define debug(...) debug_internal(#__VA_ARGS__, __VA_ARGS__)

template <class T, class... Args>
void debug_internal(const char* s, T&& first, Args&&... args) {
    constexpr const char* open_brakets = sizeof...(args) == 0 ? "" : "(";
    constexpr const char* close_brakets = sizeof...(args) == 0 ? "" : ")";
    std::cerr << open_brakets << s << close_brakets << ": "
              << open_brakets << std::forward<T>(first);
    ((std::cerr << ", " << std::forward<Args>(args)), ...);
    std::cerr << close_brakets << endl;
}

以下のようなことが出来る

//テンプレート

int main(){
  int a=3,b=4;
  char c='a';
  double d=3.5;
  string s="hoge";
  debug(a,b,c,d,s);
}
// (a,b,c,d,s): (3, 4, a, 3.5, hoge) と出力される

また、出力を自分で定義しておくことで以下のように他の型も扱えるようにしてある

//テンプレート

int main(){
  vector<vector<int>> v(5);
  REP(i,5)REP(j,i*2)v[i].push_back(i+j);

  pair<int,int> P={1,2};
  
  set<int> se;
  REP(i,10)if(i&1)se.insert(i);
  
  map<vector<int>,int> mp;
  for(auto ve:v)mp[ve]=ve.size();
  
  debug(v); // v: [[],[1,2],[2,3,4,5],[3,4,5,6,7,8],[4,5,6,7,8,9,10,11]]
  debug(se,P); // (se,P): ({1,3,5,7,9}, [1,2])
  debug(mp); // mp: [[]:0][[1,2]:2][[2,3,4,5]:4][[3,4,5,6,7,8]:6][[4,5,6,7,8,9,10,11]:8]
}

提出の際には働いてほしく無いので、手元のコンパイル時に __LOCAL を define させておき、コードに以下を書いておく

#ifdef __LOCAL
 #include <debug>
#else
 #define debug(...) void(0)
#endif

手元で debug の結果を見たく無い時は 2>/dev/null でエラー出力を無視する