スコルの知恵袋

主にプログラミング関係の気になったこと等をまとめたブログ

「良いぞ!便利だぞ!」リスト (青変記念)

苦節16ヵ月、やっと青になれました。いや~~学生の間に達成できてよかった。そして、いっぱいいいねもらえてうれしい(満たされる承認欲求)。

f:id:scol:20200914100428p:plain:w500
やったぜ!

青変記念に、僕が今まで競プロをしてきた中で「これ良いぞ!/ 便利だぞ!」と感じたものをジャンルを問わず超適当にリストアップした。

目次

1. AtCoder Problems

これはみんな知ってる説明不要の神サイト。今年の3月~4月は暇が多かったのでここで毎日開かれていたあさかつ/よるかつに参加しまくった。これのおかげでかなり力が付いたと思う。 今もたまに参加している。 @kenkoooo さんとバチャを立ててくださっている方々に圧倒的感謝......!

2. AtCoder Rating Simulator

次のコンテストでどんなパフォを取ったらどんなレートになるかを表してくれるサイト。色変が近づくと訪問回数が指数関数的に上昇する。

3. AtCoder Performances

パフォーマンスの推移を表示してくれるサイト。高いパフォを取ったときにイキるために訪問することが多い。

4. AtCoder Charts

レーティンググラフとパフォーマンスグラフを重ねて表示してくれるサイト。AtCoder 公式ページのグラフと違って横軸が時間ではなく回数なので、回ごとのレート変化やパフォーマンス変化を見たいときにおすすめ。

5. Wolfram|Alpha: Computational Intelligence

みんなの味方、Wolfram Alpha くん。競プロ的には数式の整理によく使われる。結構ややこしい式でもちゃんと整理してくれるのですごい。 ただ、ときどきグレて入力した式をそのまま返してきたり、「お前の言ってること、わからんわ(笑)」と言ってきたり、超幾何関数とかいう訳の分からない関数を使ってイキってきたりする。

6. GRAPH × GRAPH:競技プログラミングにおけるグラフ可視化サイト

グラフの情報をテキストで入力するとそれを描画してくれるサイト。愛用してます。

7. WSL + VSCode (競プロ環境)

※ 僕の使用言語は C++ です。

僕は、VSCodeスニペット・コード補完・自動フォーマット・デバッグ機能が無いと生きていけません。

はじめは Visual Studio でやってたんだけど、途中から WSL + VSCode に変えた。 Visual Studio よりこっちのが動作が軽くて好き。あと、コンパイラは手元とジャッジサーバで揃えておきたい。 ただ、デバッグの便利さは Visual Studio の方が上だな~と感じる。

tasks.json ファイルを作っておくと、コンパイルコマンド g++ hogehoge... を手打ちする必要がなくなり便利。

8. コンパイラオプション -Wall -Wextra

このオプションを付けると、if (N = 1) {... とか if (N & 1 == 0) {... とかのヤバいコードをコンパイラが叱ってくれるようになる。 初期化されない変数が存在するときも叱ってくれるため、入力の書き忘れを防ぐこともできる。便利。

9. コンパイラオプション -fsanitize=undefined,address -fno-omit-frame-pointer

このオプションを付けると、実行時に配列外参照や未定義動作(オーバーフローとか)を検知してくれるようになる。どの行がダメなのかも教えてくれるからデバッグがはかどる。これはめちゃめちゃおすすめ。 このオプションに救われた回数は正直数えきれない。

僕の環境だとこのオプションを指定すると変なエラーが出るようになったけど、~/.bashrc の最後に

export ASAN_OPTIONS=detect_leaks=0

を追記すると出なくなった。

10. Alt + ↑ or Alt + ↓

多くのテキストエディタで使えるショートカットで、行を上下に移動できる。これは結構多用する。

11. 変数にカーソルを合わせ F2 を押す (VSCode)

変数の名前を一括で変更することができる。これも時々使う。

12. 自作出力クラス prints

これは作って本当に良かったと感じている。コーディングがかなり快適になった。

自作出力クラス prints の実装はこちら (C++17 以上を要求します)

class Prints {
private:
    class __Prints {
    public:
        __Prints(const char* sep, const char* term) : sep(sep), term(term) {}
        template <class... Args>
        auto operator()(const Args&... args) const -> decltype((std::cout << ... << std::declval<Args>()), void()) { print(args...); }
        template <typename T>
        auto pvec(const T& vec, size_t sz) const -> decltype(std::cout << std::declval<decltype(std::declval<T>()[0])>(), void()) {
            for (size_t i = 0; i < sz; i++)
                std::cout << vec[i] << (i == sz - 1 ? term : sep);
        }
        template <typename T>
        auto pmat(const T& mat, size_t h, size_t w) -> decltype(std::cout << std::declval<decltype(std::declval<T>()[0][0])>(), void()) {
            for (size_t i = 0; i < h; i++)
                for (size_t j = 0; j < w; j++)
                    std::cout << mat[i][j] << (j == w - 1 ? term : sep);
        }
 
    private:
        const char *sep, *term;
        void print() const { std::cout << term; }
        void print_rest() const { std::cout << term; }
        template <class T, class... Tail>
        void print(const T& head, const Tail&... tail) const { std::cout << head, print_rest(tail...); }
        template <class T, class... Tail>
        void print_rest(const T& head, const Tail&... tail) const { std::cout << sep << head, print_rest(tail...); }
    };
 
public:
    Prints() {}
    __Prints operator()(const char* sep = " ", const char* term = "\n") const { return __Prints(sep, term); }
};

C++ の cout って、例えば4つの変数 a, b, c, d を空白区切りで出力しようとすると

cout << a << " " << b << " " << c << " " << d << "\n";

とか入力しなきゃいけなくて、クソ面倒。なので、自作出力クラス prints でこれを

prints()(a, b, c, d);

と書けるようにした。(a, b, c, d) の手前のカッコは何????と思われるかもしれないが、ここでは、区切り文字と終端文字を指定する。 たとえば、

prints(" + ", " = ")(a, b, c, d);

とすると a + b + c + d =(改行なし) が出力される。 区切り文字と終端文字はそれぞれ空白と改行がデフォルトとなっていて、引数が省略されるとデフォルトが適用される。

ほかに、1次元配列や2次元配列を出力する機能も付けてある。

13. 手元環境でのみ標準入出力をファイル入出力に切り替える

コードを

void solve() {
    // 実装
}

int main() {
#ifdef FILEINPUT
    std::ifstream ifs("./input.txt");
    std::cin.rdbuf(ifs.rdbuf());
#endif
#ifdef FILEOUTPUT
    std::ofstream ofs("./output.txt");
    std::cout.rdbuf(ofs.rdbuf());
#endif
    solve();
    return 0;
}

みたいな構造にしておいて、手元環境のコンパイラオプションに -DFILEINPUT -DFILEOUTPUT を追加すると、手元環境においてのみ標準入出力をファイル入出力に切り替えることができる。

デバッグ作業を行う上では標準入出力よりファイル入出力の方が便利だと思っている。

14. 複数テストケースにおけるアレ

こういうやつ↓

inline void solve_one() {
    // 実装
}

void solve() {
    int Q;
    cin >> Q;
    while(Q--) solve_one();
}

int main() {
    solve();
    return 0;
}

割と最近まで while で複数テストケースをまわす書き方をしていたけど、多くの人が上の書き方をしていたのでマネをした。 return で 単一のテストケースを抜けられるの超便利だね。

15. ラムダ再帰

ラムダ再帰というのは例えばこういうやつ↓

// 木の各頂点の子孫の個数を求める DFS
auto dfs = [&](auto&& self, int idx, int par) -> int {
    int sum = 0;
    for (auto u : g[idx]) {
        if (u == par) continue;
        sum += self(self, u, idx);
    }
    nch[idx] = sum;
    return sum + 1;
};
dfs(dfs, 0, -1);

これはめっちゃいい。何が良いかというとよく言われているように処理を上から下へ書けるのが良い。 あと、変数をいちいちグローバル変数にしなくてもいいのが最高。計算時間は普通の再帰関数と変わらない。

16. 構造化束縛

C++17 で追加された機能。こういうやつ↓ 控えめに言ってくっそ便利だよね。

tuple<int, long long, vector<int>> tup;
auto [a, b, c] = tup;  // こういうやつ

// 範囲 for 文でも使える
vector<pair<int, int>> vec1;
for (auto [x, y] : vec1) {
    // 処理
}

// std::array や 生配列もできる
array<int, 4> vec2;
auto [v1, v2, v3, v4] = vec2;
double vec3[3];
auto [v5, v6, v7] = vec3;

// 構造体も ok
struct Edge {
    int from, to;
    long long cost;
} edge{1, 2, 3};
auto [from, to, cost] = edge;

17. chmin / chmax

宗教上の理由(テンプレ部分は最小限にしたという気持ち)でずっと chmin / chmax を使ってこなかったんだけど、 上のクソデカ自作出力クラス prints くんがテンプレ入りしたことでもうどうでもいい気持ちになり、最近になって使い始めた。

使ってみると多くの人がテンプレに入れている理由が分かった。これめっちゃ便利だわ。特に、bool を返すのが良いね。

他に多くの人が使っていて僕が使ってないものに rep マクロ と online-judge-tools がある。まぁ、これらも使ってみたら便利なんだろな (rep の方はちょっと抵抗があるけど)。

18. ModInt

ModInt はかなり初期のころから使ってる。

「競プロライブラリの中でひとつだけ選べるとしたら何を選ぶ?」と聞かれたら、「ModInt」と答えると思う。 ModInt 楽ちん過ぎるんだよなぁ。

例えば、ABC178-C を ModInt を使って解くとこうなる。

// リンク:https://atcoder.jp/contests/abc178/submissions/16738040
// ModInt 略
constexpr ll MOD = 1000000007;
using Mint = ModInt<MOD>;

void solve() {
    int N;
    cin >> N;
    prints()(Mint(10).pow(N) - 2 * Mint(9).pow(N) + Mint(8).pow(N));
}

19. セグ木

セグ木便利すぎて、すぐにセグ木で殴れないか考える人間になってしまった。ただ、これが裏目に出ることも多々ある。

20. モンスターエナジー パイプラインパンチ

眠くならない。便利。

おしまい。