競技プログラミング用C++テンプレート
C++で競技プログラミングをやる際の最小限のテンプレートを考えてみます。 ここに書いていないことも色々考えてはいますが、とりあえずさわりだけ。
AtCoderのgcc(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引数の型を別にしておくことで、x
が i64
型であっても
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を入れておくと見やすいと思います。
本来はデバッグ用機能もあった方がいいですが、これを扱うとかなりコード量 が増えてしまうためとりあえず今回はここまでで。