競技プログラミング用C++テンプレート

C++競技プログラミングをやる際の最小限のテンプレートを考えてみます。 ここに書いていないことも色々考えてはいますが、とりあえずさわりだけ。

AtCodergcc(C++14)を想定しています(近々C++17に更新されそうですが)。

標準ライブラリ

#include <bits/stdc++.h>
using namespace std;

私は <bits/stdc++.h> を使ってしまっています。これは賛否両論あるでし ょうが、経験上gcc/clang両対応のコードを書くのは案外面倒だったので、そ れならgcc決め打ちでいいだろうという判断です。

using namespace std; も通常は問題ないでしょう。

文字列化/文字列結合マクロ

#define CPP_STR(x) CPP_STR_I(x)
#define CPP_CAT(x,y) CPP_CAT_I(x,y)
#define CPP_STR_I(args...) #args
#define CPP_CAT_I(x,y) x ## y

#define CPP_STR(x) #x とすると CPP_STR(__LINE__)__LINE__ に 展開されてしまう罠の対策です(参考: PRE05-C. 字句の結合や文字列化を行う際のマクロ置換動作をよく理解する )。また、CPP_STR_I の引数を可変長にすることで CPP_STR_I(x,y) -> "x,y" のような変換を可能にしています(デバッグマクロ作成時などに便利)。

AtCoderなどの場合は Boost.Preprocessor を使ってもいいかも。

assert

#define ASSERT(expr...) assert((expr))

assert(f<int,int>()); みたいなのがコンパイルエラーになる罠の対策です (プリプロセッサC++文法を解さないため、素の assert を使う場合は全体 を括弧でくくる必要がある)。

数値型

using i8  = int8_t;
using u8  = uint8_t;
using i16 = int16_t;
using u16 = uint16_t;
using i32 = int32_t;
using u32 = uint32_t;
using i64 = int64_t;
using u64 = uint64_t;

using f32 = float;
using f64 = double;
using f80 = __float80;

型名は好み次第ですが、私はRust風に命名しています。 どうせgcc決め打ちなので __float80 も使っています。

オーバーフローでACを逃すのはつまらないので、基本的に整数型は i64 だ けを使うのがいいと思います。多少メモリは食いますが、今時それでMLEにな るのは問題の方が悪いと思います。最近のAtCoderはメモリ制限1GBがデフォで すし。

定数

constexpr i64 INF = 1'010'000'000'000'000'017LL;

constexpr i64 MOD = 1'000'000'007LL;

constexpr f64 EPS = 1e-12;

constexpr f64 PI = 3.14159265358979323846;

INF が満たすべき条件を考えると

  • 1018 より大きい
  • 2倍してもオーバーフローしない

あたりが妥当でしょう。一応素数にもしていますが、これはあまり意味がない かも(一応、mod をとること前提のコードを mod をとらない問題に流用するた めに MOD=INF とすることを想定)。

EPS は割と適当です。問題によっては要調整かも?

FOR/REP マクロ

#define FOR(i, start, end) for(i64 i = (start), CPP_CAT(i,xxxx_end)=(end); i < CPP_CAT(i,xxxx_end); ++i)
#define REP(i, n) FOR(i, 0, n)

前述の通り、ここでも i64 を使います。

end が複数回評価されるのを防ぐため、変数に代入します。C++のマクロは 非衛生的なので、変数名が衝突しないよう xxxx_end のようなsuffixを付け ています(_end のように先頭をアンダースコアにするのは避けた方が無難だ と思います。REP(_,N) としたとき __end のように C++の予約済み識別子 に展開されてしまうため)。

ALL マクロ

#define ALL(f,c,...) (([&](decltype((c)) cccc) { return (f)(std::begin(cccc), std::end(cccc), ## __VA_ARGS__); })(c))

sort(begin(v),end(v))ALL(sort,v) と略記できます。ここでもまず 衝突しないような変数名を使っています。なお、最後の , ## __VA_ARGS__gcc拡張機能 です。

参考: 競技プログラミングでコーディングの際気を付けていること - うさぎ小屋

コンテナ/配列サイズ

template<typename C>
i64 SIZE(const C& c) { return static_cast<i64>(c.size()); }

template<typename T, size_t N>
i64 SIZE(const T (&)[N]) { return static_cast<i64>(N); }

コンテナの size() メンバ関数などを使うと符号なしの size_t が混入し てきて面倒なので、自前実装します。

chmax/chmin

template<typename T, typename U, typename Comp=less<>>
bool chmax(T& xmax, const U& x, Comp comp={}) {
    if(comp(xmax, x)) {
        xmax = x;
        return true;
    }
    return false;
}

template<typename T, typename U, typename Comp=less<>>
bool chmin(T& xmin, const U& x, Comp comp={}) {
    if(comp(x, xmin)) {
        xmin = x;
        return true;
    }
    return false;
}

値が更新されたかどうかを返すようにしておくと何かと便利です。

また、第1,2引数の型を別にしておくことで、xi64 型であっても chmax(x,0) のように書けます(型が同じだと chmax<i64>(x,0) のように テンプレート引数が必要)。

一応比較関数も渡せるようにしておきます(使用頻度は低いですが)。

初期化

struct ProconInit {
    static constexpr int IOS_PREC = 15;
    static constexpr bool AUTOFLUSH = false;

    ProconInit() {
        cin.tie(nullptr);
        ios::sync_with_stdio(false);
        cout << fixed << setprecision(IOS_PREC);
        if(AUTOFLUSH)
            cout << unitbuf;
    }
} PROCON_INIT;

を行います。グローバルオブジェクトのコンストラクタ内にこれらを書いてお けば main 実行前に自動で実行されます。

全体

// header {{{
#include <bits/stdc++.h>
using namespace std;

#define CPP_STR(x) CPP_STR_I(x)
#define CPP_CAT(x,y) CPP_CAT_I(x,y)
#define CPP_STR_I(args...) #args
#define CPP_CAT_I(x,y) x ## y

#define ASSERT(expr...) assert((expr))

using i8  = int8_t;
using u8  = uint8_t;
using i16 = int16_t;
using u16 = uint16_t;
using i32 = int32_t;
using u32 = uint32_t;
using i64 = int64_t;
using u64 = uint64_t;

using f32 = float;
using f64 = double;
using f80 = __float80;
// }}}

constexpr i64 INF = 1'010'000'000'000'000'017LL;

constexpr i64 MOD = 1'000'000'007LL;

constexpr f64 EPS = 1e-12;

constexpr f64 PI = 3.14159265358979323846;

// util {{{
#define FOR(i, start, end) for(i64 i = (start), CPP_CAT(i,xxxx_end)=(end); i < CPP_CAT(i,xxxx_end); ++i)
#define REP(i, n) FOR(i, 0, n)

#define ALL(f,c,...) (([&](decltype((c)) cccc) { return (f)(std::begin(cccc), std::end(cccc), ## __VA_ARGS__); })(c))

template<typename C>
i64 SIZE(const C& c) { return static_cast<i64>(c.size()); }

template<typename T, size_t N>
i64 SIZE(const T (&)[N]) { return static_cast<i64>(N); }

template<typename T, typename U, typename Comp=less<>>
bool chmax(T& xmax, const U& x, Comp comp={}) {
    if(comp(xmax, x)) {
        xmax = x;
        return true;
    }
    return false;
}

template<typename T, typename U, typename Comp=less<>>
bool chmin(T& xmin, const U& x, Comp comp={}) {
    if(comp(x, xmin)) {
        xmin = x;
        return true;
    }
    return false;
}
// }}}

// init {{{
struct ProconInit {
    static constexpr int IOS_PREC = 15;
    static constexpr bool AUTOFLUSH = false;

    ProconInit() {
        cin.tie(nullptr);
        ios::sync_with_stdio(false);
        cout << fixed << setprecision(IOS_PREC);
        if(AUTOFLUSH)
            cout << unitbuf;
    }
} PROCON_INIT;
// }}}

//--------------------------------------------------------------------

void solve() {
    

    // cout << ans << "\n";
}

signed main() {
    

    solve();

    return 0;
}

Vimmerの人は適宜foldingを入れておくと見やすいと思います。

本来はデバッグ用機能もあった方がいいですが、これを扱うとかなりコード量 が増えてしまうためとりあえず今回はここまでで。