AtCoder ABC 156 復習メモ

A - Beginner

えっ、難しい…
体調不良で更にIQが低下してしまい文意が全く頭に入ってきません
ひとまず制約と入力をみて入力を書き、問題文の計算式を丸写しします
そしてサンプル1を眺めていると何をすべきかわかってきました

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

int main() {
  int N, R;
  cin >> N >> R;
  cout << ((N >= 10) ? R : R + (100 * (10 - N))) << "\n";
}

coutの中を括弧で閉じてないとか左右の括弧の数が違うとか
5回くらい構文エラーを出して提出。これはひどい

B - Digits

与えられた整数NをK進数にしたときの桁数を答える問題
IQ2でも素早く理解できました
応用情報の勉強で2進数変換を何度かやらされ

2進数の変換方法
f:id:ninoi:20200229174946p:plain
こういうの

なんとなくKで割れた回数をカウントすればいいのかな~~と思って書いたら
サンプルが合ったので提出しました

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

int main() {
  int N, K, ans = 0;
  cin >> N >> K;
  while (N) N /= K, ans++;
  cout << ans << "\n";
}

応用情報の勉強が役に立ったのはABC140のA問題に続いて2回目です
やっててよかった応用情報(そうか??)

C - Rally

相変わらず文意を把握出来ませんが制約をみて全探索だろうと考えました
制約上intで十分と思われますがにのいという人物、信用ならないので
2e18を初期値に問題文の通り全探索しました
置換したのでfor文の変数もlongになってますね…

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

int main() {
  long N;
  cin >> N;
  vector<long> X(N);
  for (long i = 0; i < N; i++) cin >> X.at(i);
  long ans = 2e18;
  for (long i = 0; i <= 100; i++) {
    long tmp = 0;
    for (long j = 0; j < N; j++) {
      tmp += (X.at(j) - i) * (X.at(j) - i);
    }
    ans = min(ans, tmp);
  }
  cout << ans << "\n";
}

ただC問題からintだとオーバーフローする問題が出てくるので
常にlongで良い気もします。using Int = long long; して
intを使わない方もよくお見掛けします。かしこい

D - Bouquet


N本から1本選ぶ場合
N本から2本選ぶ場合
N本から3本選ぶ場合
.
.
.
N本からN-1本選ぶ場合
N本からN本(全て)選ぶ場合

の合計を求めて


N本からA本選ぶ場合
N本からB本選ぶ場合

を引いてあげれば良さそうです
とはいえ①の求め方がわからないのでけんちょんさんのこちらの記事の

qiita.com
5. 二項係数 nCr からライブラリを拝借して実験してみました

N = 10の場合

 _{10} \mathrm{C} _1 = 10
 _{10} \mathrm{C} _2 = 45
 _{10} \mathrm{C} _3 = 120
 _{10} \mathrm{C} _4 = 210
 _{10} \mathrm{C} _5 = 252
 _{10} \mathrm{C} _6 = 210
 _{10} \mathrm{C} _7 = 120
 _{10} \mathrm{C} _8 = 45
 _{10} \mathrm{C} _9 = 10
 _{10} \mathrm{C} _{10} = 1

なんとなく _{10} \mathrm{C} _0が1になりそうなこと
それも考慮したほうがよさそうな雰囲気を察して
すべて足し合わせると1024になりました
とてもいかがわしい数字です…

というわけで2重ループを回してN = 1 から N = 10 まで同様に合計を求めてみました

// 前略

int main() {

    COMinit();

    for (int i = 1; i <= 10; i++) {
      int tmp = 0;
      for (int j = 0; j <= i; j++) {
        tmp += COM(i, j);
      }
      cout << tmp << "\n";
    }
}
2
4
8
16
32
64
128
256
512
1024
勝ち申したな…

しかしここで2の1000000000乗(の剰余)の求め方がわからないことに気付きます
色々調べて弄ってみるも終始コード139(スタックオーバーフロー)で
プログラムが動かずコンテストは終わってしまいました…

終了後にわかったこと

①については調べて出てきた繰り返し二乗法で求められていたことがわかりました
原因は②で、拝借した二項係数ライブラリで扱えるのは
1 ≦ k ≦ n ≦  10^7 程度ということを知らず 10^9を突っ込んだままにしていたからでした…

こちらの記事でしっかり書かれています

drken1215.hatenablog.com

・数学弱者で組み合わせをライブラリに頼り切っていたこと
・ライブラリの中身を理解できていなかったこと
・問題発生ポイントの切り分けが出来なかったこと

反省点は多いです

コンテスト後に頂いただるさんのリプライが正鵠を射ていたのですが
ちょっと何を言ってるか分からずいいねでお茶を濁しました

E - Roaming

D問題で拝借した二項係数ライブラリと

qiita.com

こちらの記事の重複組合せの理解があれば実装できたようです
ただそこに至るまでの考察も難しく先は果てしないなと思いました



おわり