競技プログラミング勉強用リスト
世の中にはよくまとまっている記事がたくさんあるので、自分用リンク集+メモ書き。
- 標準出入力
- 計算量オーダー
- 全探索
- DP
- DFS
- BFS
- 二分探索
- 累積和
- しゃくとり法
- 半分全列挙
- 最短路問題
- ベルマンフォード法
- ダイクストラ法
- ワーシャルフロイド法
- 最小全域木
- プリム法
- クラスカル法
- データ構造
- UnionFind
- SegmentTree
- Binary Indexed Tree
- STL
- vector
- deque
- set
- map
- priority_queue
- 計算用ライブラリ
- gcd, lcm
- C++17よりstd::gcdが使用可能
- gcd - cpprefjp C++日本語リファレンス
- combination
- fast power
- エラトステネスの篩
- 素因数分解
- 約数の列挙
- gcd, lcm
- Mod
- bit演算
- 競プロ用マクロ
STLチートシート
迷ったら結局リファレンスを見るのが一番速い説
vector
#include <vector> using namespace std; // a = {0, 0, 0, 0, 0} vector<int> a(5, 0); // a = {1, 1, 1} a.assign(3, 1); // a = {1, 1, 1, 2} a.push_back(2); // a = {1, 3, 1, 1, 2} O(N)!!!!!! a.insert(a.begin() + 1, 3); // 3 a[1]; // 1 a.front(); // 2 a.back(); // 5 a.size();
- insertはO(N)なので、使わない
remove duplications
- 参考
- c++ - What's the most efficient way to erase duplicates and sort a vector? - Stack Overflow
- 計算速度への言及あり、重複が少なければvectorのまま, 多ければsetを経由が速い
- 以下引用
sort( vec.begin(), vec.end() ); vec.erase( unique( vec.begin(), vec.end() ), vec.end() ); set<int> s; unsigned size = vec.size(); for( unsigned i = 0; i < size; ++i ) s.insert( vec[i] ); vec.assign( s.begin(), s.end() );
priority_queue
// declaration, default comparer is less: top is the largest element priority_queue<pair<int, int>> que; auto p = make_pair(1, 2); que.push(p); que.emplace(2, 1); // t = {2, 1} auto t = que.top(); que.pop(); // specify comparer to get smallest element priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> que2; que2.emplace(1, 2); que2.emplace(2, 1); // u = {1, 2} cout << u.first << " " << u.second << endl; // choose the element which has smallest pair.second auto comp = [](pair<int, int> l, pair<int, int> r) { return l.second > r.second; }; priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(comp)> que3(comp); que3.emplace(1, 3); que3.emplace(3, 2); que3.emplace(2, 1); // que3: top = {2, 1} < {3, 2} < {1, 3}
- テンプレート引数に与える比較関数でもっとも優先度が低い要素がtopになるので注意
- 自作クラスを要素にする場合、比較演算子を作成するか、ラムダ式で比較関数を作成する
- 普通はpairかtupleを使ってしまうのが便利
string
#include<string> // s = "AAAAAAAAAA" string t(10, 'A'); // s = "ABC" string s("ABC"); // 'B' s[1]; // s = "ABCD" s += "D"; // erase i-th element (0-indexed) s.erase(s.begin()+i); // erase from i to i+n-1 s.erase(i, n); // erase elements in [begin, end) s.erase(s.begin(), str.end()); // initPos is the position we start to look it up int pos = s.find("E", initPos); if (pos == string::npos) cout << "not found" << endl; // get substring from start to start+num-1 s.substr(start, num); // cast to int s = "123"; int a = stoi(s); // cast to long long long long b = stoll(s); // cast numbers to string string u = to_string(a);
- []演算子で取り出した1文字はcharなので、char literal ('0'など)と比較する
セグメント木
参考
プログラミングコンテストチャレンジブック [第2版] p.153-
初心者の初心者による初心者のための典型segment tree - DEGwerの競技プログラミングと時々数学
セグメント木をソラで書きたいあなたに - hogecoder
遅延評価セグメント木をソラで書きたいあなたに - hogecoder
セグメント木
- どういう写像が合成されていくか意識するのが大切
RMQセグメント木
#include <vector> #include <climits> using namespace std; const int INF = INT_MAX; // 0-indexed RMQ segment tree class SegTree { private: int n; vector<int> dat; public: SegTree(int n_) { n = 1; while (n < n_) n *= 2; dat.assign(2 * n - 1, INF); } void update(int k, int a) { k += n - 1; dat[k] = a; while (k > 0) { k = (k - 1) / 2; dat[k] = min(dat[2 * k + 1], dat[2 * k + 2]); } } int query(int a, int b, int k, int l, int r) { if (r <= a || b <= l) return INF; if (a <= l && r <= b) return dat[k]; else { int vl = query(a, b, 2 * k + 1, l, (l + r) / 2); int vr = query(a, b, 2 * k + 2, (l + r) / 2, r); return min(vl, vr); } } // get min in [a, b) int query(int a, int b) { return query(a, b, 0, 0, n); } };
- 蟻本の関数をclassで書いただけ
- 半開区間の最小値を求めることができる
- min()をSegmentTreeに載せている。単位元はINF
- update, query共にO(logN)
- 簡単のため構築時は全ての値がINFなので、初期化にO(NlogN)かかる。最初から数列を与えるようにすれば初期化はO(N)
INT_MAXは実装依存とのこと INT_MAX - cpprefjp C++日本語リファレンス
競プロ用出入力メモ
参考
c++ - Significance of ios_base::sync_with_stdio(false); cin.tie(NULL); - Stack Overflow
iostream
#include <iomanip> #include <iostream> #include <string> using namespace std; int main() { ios_base::sync_with_stdio(false); cin.tie(0); int N; cin >> N; cout << N << "\n"; double x; cin >> x; cout << fixed << setprecision(10) << x << "\n"; string s; cin >> s; cout << s << endl; }
- 遅いが、出入力の行数が少ない場合には十分
- 型を自動判別してくれるため楽
- 小数点以下の桁数を指定する場合、iomanipをincludeしてfixed << setprecision()
- 速度を気にする場合
- endlではなく改行文字\nの方が速い
- mainはじめの二行を追加すると高速化できる場合がある
- sync_with_stdio(false)を使用した場合stdioを使用してはいけない
- cin.tie(0)はcinとcoutの連携を切ってしまい、出力が出る前に次の入力が行われる危険がある。競プロの形式では問題ないはず