AtCoder参戦日記 ABC185 2020/12/13 12回目
AtCoderの12回目の参加となった2020/12/13のABC185の記録です。9か月も下書きのまま放置してしまっていましたが、前回記事に引き続き、AtCoderの感を取り戻すための復習記事です。
100分の競技時間ぎりぎりで5完でした。
Dまでは35分ほどで完了しました。Eは問題文を1回読んですぐにあきらめ、Fに進みました。Fはコードを書くのに時間がかかってしまいましたが、ぎりぎりACを取れました。
パフォーマンスは1160で、AtCoder Beginner Contestとしては過去2番目にいい成績ではありました。
問題 | 結果 | 言語 | 競技後 |
---|---|---|---|
A | AC | JavaScript | |
B | AC | Java | Scala |
C | AC | Ruby | C++ |
D | AC | Scala | |
E | |||
F | AC | C++ | Java |
5問できたのもめずらしいのですが、5言語使ったのは初めてです。1問目の言語選択は理由はないのですが、他は問題ごとに最適な言語を選んだつもりです。
- A - ABC Preparation
- B - Smartphone Addiction
- C - Duodecim Ferra
- D - Stamp
- E - Sequence Matching
- F - Range Xor Query
過去の参戦記録
参加日 | # | コンテスト名 | 結果 | 使用言語 |
---|---|---|---|---|
2020/08/29 | 1 | ABC177 | 3完 / 614 | Scala, Scala, Java |
2020/09/13 | 2 | ABC178 | 5完 / 1466 | Python, Scala, Scala, C++, Scala |
2020/09/19 | 3 | ABC179 | 3完 / 1004 | Java, Scala, Scala |
2020/10/03 | 4 | ARC104 | 2完 / 1067 | Python, Scala |
2020/10/10 | 5 | HHKB2020 | 3完 / 902 | C++, Scala, Java |
2020/10/11 | 6 | ARC105 | 2完 / 1069 | Ruby, Java |
2020/11/01 | ABC180 | 4完 / (937) | Ruby, Scala, C++, Java | |
2020/11/01 | 7 | ABC181 | 4完 / 1136 | C++, Scala, Java, Java |
2020/11/07 | ABC171 | 5完 / (1557) | C++, Scala, Ruby, Java, Java | |
2020/11/08 | 8 | ABC182 | 4完 / 1072 | Ruby, Scala, Scala, Java |
2020/11/15 | 9 | ABC183 | 4完 / 871 | Ruby, Ruby, Java, Java |
2020/11/21 | 10 | ARC108 | 3完 / 1979 | Ruby, Java, Java |
2020/12/05 | 11 | ARC110 | 3完 / 1562 | Ruby, Scala, Java |
2020/12/13 | 12 | ABC185 | 5完 / 1160 | JavaScript, Java, Ruby, Scala, C++ |
※結果欄は、時間内にAC取れた問題数とパフォーマンスです。パフォーマンスの括弧の数字はバーチャル参加の推定値です。
A - ABC Preparation
言語の練習のためにJavaScriptにしました。競技本番でJavaScriptを書いたのは初めてです。
const lines = require('fs').readFileSync('/dev/stdin', 'utf8').split("\n"); const [a1, a2, a3, a4] = lines[0].split(" ").map(s => Number(s)); console.log(Math.min(a1, a2, a3, a4));
B - Smartphone Addiction
手続き型で書きたかったので関数型のScalaではなくJavaで書きました。
class Main { public static void main(String[] args) { var sc = new java.util.Scanner(System.in); int n = sc.nextInt(); int m = sc.nextInt(); int t = sc.nextInt(); var abs = new int[2*m]; for (int i = 0; i < m; i++) { abs[2*i ] = sc.nextInt(); abs[2*i+1] = sc.nextInt(); } int c = n; int ct = 0; for (int i = 0; i < m; i++) { int a = abs[2*i ]; int b = abs[2*i+1]; c -= a - ct; if (c <= 0) { System.out.println("No"); return; } c += b - a; if (c > n) c = n; ct = b; } c -= t - ct; if (c <= 0) { System.out.println("No"); return; } System.out.println("Yes"); } }
9か月後のいま、Scalaで書き直しました。
object Main extends App { val sc = new java.util.Scanner(System.in); val n, m, t = sc.nextInt(); var n1: Int = n; var t1: Int = 0; var result: Boolean = true; (0 until m).foreach { _ => val a, b = sc.nextInt(); n1 = n1 - (a - t1); if (n1 <= 0) { result = false; } n1 = n1 + (b - a); if (n1 > n) { n1 = n; } t1 = b; } n1 = n1 - (t - t1); if (n1 <= 0) { result = false; } if (result) { System.out.println("Yes"); } else { System.out.println("No"); } }
C - Duodecim Ferra
組み合わせ数を計算するだけですが、64bit整数でオーバーフローを回避する方法がすぐには思いつかず、Rubyの整数で計算しました。
l = gets.strip.to_i s = 1 for i in 12..(l-1) s = s * i end for i in 2..(l-12) s = s / i end puts(s)
9か月後のいま、他の人の回答を見て、演算の順序を気にすればいいとわかって、書いたコードが以下です。
#include <bits/stdc++.h> using namespace std; int main() { int l; cin >> l; long long result = 1; for (int i = 1; i <= 11; i++) { result *= l - i; result /= i; } cout << result << endl; }
D - Stamp
関数型で書くのが楽そうだったのでScalaにしました。
object Main extends App { val sc = new java.util.Scanner(System.in); val n, m = sc.nextInt(); val as = Array.fill(m)(sc.nextInt()); val as_sorted = as.sorted; val intervals = (0 to m).map { i => if (m == 0) { n; } else if (i == m) { n - as_sorted(i-1); } else if (i == 0) { as_sorted(i) - 1; } else { as_sorted(i) - as_sorted(i-1) - 1; } }.filter(_ > 0); val answer = if (intervals.size == 0) { 0; } else { val min = intervals.min; intervals.map { d => (d-1) / min + 1; }.sum; } println(answer); }
E - Sequence Matching
問題文を1回読んで、すぐにあきらめました。
F - Range Xor Query
少し考えて解法を思いついたものの、コードを書くのに時間がかかってしまいました。解法はあとで解説を読んで、セグメント木というらしいです。最後につまらないバグにもはまってしまい、問題文を読み始めてからAC取るまでに1時間近くかかってしまいました。
解法を思いついた時点で、処理速度の勝負だと思ったので、C++にしました。
#include <bits/stdc++.h> using namespace std; int main() { int n, q; cin >> n >> q; int count = 1 << 19 + 1; int as[count]; for (int i = 0; i < n; i++) { cin >> as[i]; } for (int i = n; i < count; i++) { as[i] = 0; } int *ass[19]; for (int di = 0; di < 19; di++) { int d = 1 << di; int c = count >> (di+1); ass[di] = new int[c]; for (int i = 0; i < c; i++) { int id = i << (di+1); int xo = 0; for (int j = 0; j < d; j++) { xo = xo ^ as[id + j]; } ass[di][i] = xo; } } for (int i = 0; i < q; i++) { int t, x, y; cin >> t >> x >> y; if (t == 1) { int z = x - 1; for (int di = 0; di < 19; di++) { if (((z >> di) & 1) == 0) { int zi = z >> (di+1); ass[di][zi] = ass[di][zi] ^ y; } } } else { int zo = 0; int z = y; for (int di = 0; di < 19; di++) { if (((z >> di) & 1) != 0) { zo = zo ^ ass[di][z >> (di+1)]; } } z = x - 1; for (int di = 0; di < 19; di++) { if (((z >> di) & 1) != 0) { zo = zo ^ ass[di][z >> (di+1)]; } } cout << zo << endl; } } }
9か月後のいま、ゼロベースで考えてJavaで書いたコードが以下です。上記とたぶん同じロジックだと思うのですが、上記を今となっては読み解けません。
class Main { public static void main(String[] args) { var sc = new java.util.Scanner(System.in); int n = sc.nextInt(); int q = sc.nextInt(); int max = 1 << 19; int[] segment = new int[max]; int[] buf = new int[20]; for (int i = 0; i < n; i++) { int a = sc.nextInt(); updateSegment(i, a, segment, buf, max); } for (int i = 0; i < q; i++) { int t = sc.nextInt(); int x = sc.nextInt(); int y = sc.nextInt(); if (t == 1) { x = x - 1; updateSegment(x, y, segment, buf, max); } else { x = x - 1; y = y - 1; int ax = fetchSegment(x, segment, buf); int ay = fetchSegment(y + 1, segment, buf); int answer = ay ^ ax; System.out.println(answer); } } } private static void updateSegment(int idx, int x, int[] segment, int[] buf, int max) { indexOfUpdateSegment(idx + 1, buf, max); int i = 0; while (true) { int s = buf[i]; if (s < 0) break; segment[s] ^= x; i++; } } private static int fetchSegment(int idx, int[] segment, int[] buf) { indexOfFetchSegment(idx, buf); int i = 0; int ret = 0; while (true) { int s = buf[i]; if (s < 0) break; ret ^= segment[s]; i++; } return ret; } // 13 -> 13, 14, 16, 32, 64, 128 private static void indexOfUpdateSegment(int idx, int[] buf, int max) { int offset = 0; while (idx < max) { int lastBit = idx & -idx; buf[offset] = idx; offset++; idx = idx + lastBit; } buf[offset] = -1; } // 13 -> 13, 12, 8 private static void indexOfFetchSegment(int idx, int[] buf) { int offset = 0; while (idx != 0) { buf[offset] = idx; offset++; int lastBit = idx & -idx; idx = idx - lastBit; } buf[offset] = -1; } }