TL;DR
1:使ってるマクロ
2:使ってるスニペット
3:やる前の準備
4:ABC問題を解くときの注意事項
5:勉強用の記事
使ってるマクロ
マクロには大きく分けて2つの使い方があります。
1、よく使うコードを省略
2、忘れがちなコードの登録
// ---------------- 標準ライブラリ ------------------- //
#include <bits/stdc++.h>
using namespace std;
#include <atcoder/all>
using namespace atcoder;
/* ----------------- よく使う ------------------------ */
#define rep(i,n) for(int i = 0; i < (n); ++i)
#define rrep(i,n) for(int i = 1; i <= (n); ++i)
#define drep(i,n) for(int i = (n)-1; i >= 0; --i)
#define srep(i,s,t) for (int i = s; i < t; ++i)
#define rng(a) a.begin(),a.end()
#define rrng(a) a.rbegin(),a.rend() // 右から読む.reverse
#define sortu(x) sort((x).begin(),(x).end())
#define sortd(x) sort((x).begin(),(x).end(), greater<int>())
#define pb push_back
#define eb emplace_back
#define sz(x) (int)(x).size()
using ll = long long int;
using vi = vector<int>;
using vl = vector<ll>;
using P = pair<int,int>;
#define fi first
#define se second
#define mp make_pair
#define mset(m,v) memset(m,v,sizeof(m))
using Graph = vector<vector<int>>;
#define v(T) vector<T>
template<typename T>istream& operator>>(istream&i,v(T)&v){rep(j,sz(v))i>>v[j];return i;} // cin>>v 配列に代入
template<typename T>ostream& operator<<(ostream&o,const v(T)&v){if(sz(v))o<<join(v);return o;} // cout<<v 配列を出力
template<typename T1,typename T2>istream& operator>>(istream&i,pair<T1,T2>&v){return i>>v.fi>>v.se;}
template<typename T1,typename T2>ostream& operator<<(ostream&o,const pair<T1,T2>&v){return o<<v.fi<<","<<v.se;} // ,に注意
template<typename T>bool mins(T& x,const T&y){if(x>y){x=y;return true;}else return false;}
template<typename T>bool maxs(T& x,const T&y){if(x<y){x=y;return true;}else return false;}
template<typename T>ll suma(const v(T)&a){ll res(0);for(auto&&x:a)res+=x;return res;}
/*----------------- 定数 ---------------------*/
const ll LINF = 1001002003004005006ll;
const int INF = 1001001001;
const int MOD1 = 1e9+7;
const int MOD9 = 998244353;
const int Max_T = 200005;
/*----------------- 出力 ---------------------*/
#define shousuu cout << fixed << setprecision(15)
#define dame0 { puts("0"); return 0;} // ; 含む
#define dame1 { puts("-1"); return 0;}
#define damen { puts("No"); return 0;}
#define yn {puts("Yes");}else{puts("No");}
#define outa cout << ans << endl;
#define out(x) cout << x << endl;
#define r0 {return 0;}
/*--------------- 忘れがち -------------------*/
#define ul(x) x^=32 // 大文字小文字を逆にする
#define dup(x,y) ((x+y-1)/y) // 切り上げ
/*--------------- 二分探索 -------------------*/
#define posi(x,v) *((lower_bound(x.begin(),x.end(),v)))
#define posl(x,v) (lower_bound(x.begin(),x.end(),v)-x.begin()) // v未満の個数 (keyを前に挿入(iterはkeyより大))
#define posl2(x,v) (x.end()-lower_bound(x.begin(),x.end(),v)) // v以上の個数
#define posu(x,v) (upper_bound(x.begin(),x.end(),v)-x.begin()) // v以下の個数 (keyを後ろに挿入)
#define posu2(x,v) (x.end()-upper_bound(x.begin(),x.end(),v)) // vより大きい個数
#define bs(x,v) (binary_search(x.begin(),x.end(),v))
//-------------------Tips-----------------------//
//mins(ans,now); ansに最小値が入る
//if(max(ans,now)) ansが小さければ更新して実行
//ll sum = suma(a);
//int a[33][4];
//vi a{1,2,3};
// vvi dp(n+1, vi(3)); n+1行、3列
bool flg = false;
// flg = true;
使ってるスニペット
長すぎてマクロにできないものはスニペットにします。
特に入力系のスニペットは大活躍するので、入れるのをオススメします。
{
// ----------------- 入力 --------------------------- //
"cin_vector_int":{
"prefix": "cinv",
"body":[
"vector<int> $1($2); rep(i, $2) cin >> $1[i];",
"$3"
],
"description": "vector<int>の受取"
},
"cin_vector_int-":{
"prefix": "cinv-",
"body":[
"vector<int> $1($2); rep(i, $2) { cin >> $1[i]; $1[i]--;}",
"$3"
],
"description": "vector<int>の受取 -1"
},
"cin_vector_int2":{
"prefix": "cinv2",
"body":[
"vector<int> $1($3),$2($3); rep(i, $3) cin >> $1[i] >> $2[i];",
"$4"
],
"description": "vector<int>の受取2"
},
"cin_vector_int3":{
"prefix": "cinv3",
"body":[
"vector<int> $1($4),$2($4),$3($4); rep(i, $4) cin >> $1[i] >> $2[i] >> $3[i];",
"$5"
],
"description": "vector<int>の受取3"
},
"cin_string":{
"prefix": "cins",
"body":[
"string s; cin >> s;",
"$1"
],
"description": "stringの受け取り"
},
"cin_int3":{
"prefix": "cin3",
"body":[
"int $1, $2, $3; cin >> $1 >> $2 >> $3;",
"$4"
],
"description": "intの受取3"
},
"cin_int2":{
"prefix": "cin2",
"body":[
"int $1, $2; cin >> $1 >> $2;",
"$3"
],
"description": "intの受取2"
},
// ----------------- 省略 --------------------------- //
"ruisekiwa0":{
"prefix": "ruiseki0",
"body":[
"vector<ll> sum($1);",
"rep(i,$1) {",
"\tif(i==0) sum[i] = $2[i];",
"\telse sum[i] = $2[i] + sum[i-i];",
"}"
],
"description": "累積和0"
},
"ruisekiwa1":{
"prefix": "ruiseki1",
"body":[
"vector<ll> sum($1+1); rep(i,$1+1) sum[i+1] = $2[i] + sum[i];"
],
"description": "累積和+1"
},
"next_permutation":{
"prefix": "nextp",
"body":[
"do{",
"$2",
"}while(next_permutation($1.begin(), $1.end()));"
],
"description": "next_permutation"
},
"bit_allsearch":{
"prefix": "bit",
"body":[
"rep(i,(1<<n)){ // v[j]に対して操作をする",
"\t//rep(j,n) if((i>>j)&1){ // iのj番目のbitが1なら……",
"\t\t$1",
"\t}",
"}"
],
"description": "bit全検索"
},
// --------------- Key Algorithms -------------------- //
"find_prime_number": {
"prefix": "isPrime",
"body": [
"bool is_prime(ll N) {",
"\tif (N == 1) return false;",
"\tfor (ll i = 2; i * i <= N; ++i) {",
"\t\tif (N % i == 0) return false;",
"\t}",
"\treturn true;",
"}",
],
"description": "finding prime number"
},
"count_divisors": {
"prefix": "cntdivide",
"body": [
"ll cnt_div(ll n) {",
"\tll cnt = 1;",
"\tfor (ll i = 1; i * 2 <= n; ++i) {",
"\t\tif (n % i == 0) cnt++;",
"\t}",
"\treturn cnt;",
"}",
],
"description": "finding prime number"
},
"gcd": {
"prefix": "gcd",
"body": ["int gcd(int a, int b) {", "\treturn a ? gcd(b, a%b) : a;", "}"],
"description": "Greatest Common Divisor"
},
"lcm": {
"prefix": "lcm",
"body": ["int lcm(int a, int b) {", "\treturn a / gcd(a, b) * b;", "}"],
"description": "Least Common Multiply"
},
"digit_sum": {
"prefix": "digitsum",
"body": [
"int digit_sum(ll n) {",
"\tint res = 0;",
"\twhile(n > 0) {",
"\t\tres += n%10; //桁和",
"\t\tres++; //桁数",
"\t\tn /= 10;",
"\t}",
"\treturn res;",
"}"
],
"description": "sum of all digits"
},
}
やる前の準備
##1:VSCodeは30分前に立ち上げる
updateが必要な時があるので、事前にすませておく。
終わったら一度プログラムを走らせておく(最初は時間がかかるから)。
##2:ノートとペンを用意する
数学的な問題を解くには下書きが必須。
雑紙ではなく、きちんとしたノートを使う。
問題が解けなかった時に見返すと、自分が何を見落としてたかはっきりする。
##3:コピペを用意する
貼り付けるだけで正解する魔法のコード。検索しやすい題をつける。
ABCD各問題への対策
##A問題
###for文を使わない
A問題はfor文なしで解けます。
入力は最大で4文字なので、if文を列挙しても時間はかかりません。
例題:ABC175A
↑ Rが何回続いたのかを判定する問題です。if文を使えば、Rが続いた回数で場合分けできます。
###切り上げ
覚え方は「切って上げる」。-1で切って、割る数で上げる。覚えるのが面倒ならマクロを作りましょう。
例題:ABC157A
Q:Nページの書類を両面印刷すると、最小で何枚の紙が必要か
//ans = (N-1+2)/2;
#define dup(x,y) (((x)+(y)-1)/(y))
ans = dup(n,2);
###大きいほうを選ぶときは、ifよりmax
//if(a > b) ans = a; // コードが長いです。
//else ans = b; // コードが長いです。
ans = max(a,b); // 1行でかけます。
###Yes,Noの出力はマクロ
#define yn {puts("Yes");}else{puts("No");}
if(a >= 30) yn; // 入力が30以上ならYes。未満ならNo。
##B問題
###小数点
小数点は誤差の温床。不要な割り算は使わないようにしましょう。掛け算にすればOKです。
//if(sqrt(a*a + b*b) == c) //小数が出る
if(a*a + b*b == c*c) // 小数が出ない
小数を出力する問題は桁数に注意しましょう。多めに出力する分には問題ないので、どーんと大きくいきましょう。「正しい値との絶対誤差または相対誤差が 10^(−9) 以下であれば正解とみなされる」なら、15桁くらい出しておきましょう。
ギリギリを狙うとWAが出るので注意。
#define shousuu cout << fixed << setprecision(15);
shousuu; // 以降の出力が小数点以下15桁になる。
###オーバーフロー
数が大きくなりそうなら、intではなくlonglongを使いましょう。マクロを使うとllで省略できます。
llでもオーバーフローするなら、割り算にしましょう。
例題:ABC169B
//ans *= a; // そのまま掛け算するとオーバーフローする場合は……
const ll LINF = 1001002003004005006ll;
if(ans < LINF / a) ans *= a; // オーバーフローしないかチェックしてから、掛け算をする。
##C問題
###コーナーケースを確認する。
例題が全部通っても過信してはいけない。
###超便利なset
setは2つの機能を持っています。
・重複した値の削除
・昇順ソート
「同じ要素があるか答えよ」という問題はもちろんですが「条件を満たす数を列挙せよ」という問題にも役立ちます。例えば「Nの約数を列挙せよ」という問題の場合、通常なら重複をif文で除外して昇順ソートが必要ですが、setなら一発です。
###重複なしループ
例えば「異なる数や点を3つ選び、条件を満たしているものの個数を求める」といった問題に使えます。
rep(i,n) rep(j,i) rep(k,j)
###解けなかったらD問題へ
CよりDが簡単なこともあります。DよりEが簡単なときもあります。
(自分はまだ一度も体験してませんが)
勉強用の記事
Visual Studio Codeで競プロ環境構築
AtCoder に登録したら次にやること ~ これだけ解けば十分闘える!過去問精選 10 問 ~
レッドコーダーが教える、競プロ・AtCoder上達のガイドライン【初級編:競プロを始めよう】
レッドコーダーが教える、競プロ・AtCoder上達のガイドライン【中級編:目指せ水色コーダー!】