AtCoder Regular Contest 105

コンテスト前のレート:1796
レート通りのパフォーマンスが得られる順位:473位(1000点、45分50秒)

A - Fourtune Cookies

問題
例1の影響で2個食べると誤読。
解き直し+ペナルティで7分近いロスとなる。

思考過程
  1. 二重ループを回し、2要素の和の2倍が全部の合計と一致すれば"Yes"。 →2ケースWA
  2. 一重ループを追加し、単独要素の2倍が全部の合計と一致すれば"Yes"を付け足す。
import java.util.Scanner;

public class Main {
    public static void main(String[] args) throws Exception {
        Scanner sc = new Scanner(System.in);
        int[] a = new int[4];
        for (int i = 0; i < 4; i++) {
            a[i] = sc.nextInt();
        }
        sc.close();

        int sum = 0;
        for (int i = 0; i < 4; i++) {
            sum += a[i];
        }
        for (int i = 0; i < 4; i++) {
            if (a[i] * 2 == sum) {
                System.out.println("Yes");
                return;
            }
        }
        for (int i = 0; i < 4; i++) {
            for (int j = i + 1; j < 4; j++) {
                int v = a[i] + a[j];
                if (v * 2 == sum) {
                    System.out.println("Yes");
                    return;
                }
            }
        }
        System.out.println("No");
    }
}

ここまで4分01秒+1ペナ。978位。


B - MAX-=min

問題
人々が解法にたどり着くの早すぎ。
愚直シミュレーションの無駄を省くことばかり考えていてだいぶ遠回りした。

思考過程
  1. 単純にシミュレーションしたのでは、X=10^9, x=1の場合に全然間に合わない。
  2. a_i \neq xの要素を全てa_i \% xに置き換えていくことを考える。
  3. x未満になる要素が1つ現れた時点で、xが変わってしまうのでおかしい。
     
  4. min(a_i, a_i \% x + x)に置き換えていって、a_i \% xの最大値を次のxにするのを繰り返せばよさそうに思えるけどめんどくさい。
  5. 適当な小さい数でここまでの流れを手計算で試していたら答えは1になったが、例1は2
  6. 手元や例1の数値の並びを睨んだ感じや、問題文の「X-x」という計算から、最大公約数という単語が頭をよぎる。
  7. 若干自信ないが、ただの全要素の最大公約数を例2で試してみたら合っていたので、提出する。
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        String[] sa = br.readLine().split(" ");
        int[] a = new int[n];
        for (int i = 0; i < n; i++) {
            a[i] = Integer.parseInt(sa[i]);
        }
        br.close();

        long ans = a[0];
        for (int i = 1; i < n; i++) {
            ans = gcd(ans, a[i]);
        }
        System.out.println(ans);
    }

    static long gcd(long a, long b) {
        return b == 0 ? a : gcd(b, a % b);
    }
}

ここまで14分16秒+1ペナ。1126位。


C - Camels and Bridge

問題
解けず。前日の反省を全く生かしておらず、一旦中断してD問題を見始めるのも遅い。
O(N!)で順列全列挙はやっていいんだろうけど、毎回全部の橋を判定するO(N!M)だと駄目そうで、先頭を一番重いので固定して(N-1)!Mならいけそうか?とか、順列ごとに、ラクダをまとめて1N頭とみなせる耐荷重はいくら以上かを前計算してO(N!Nlog v + M)のようにできないかとか、色々考えたが、結局順列ごとの最適解を求める方法がきちんと正当な方法になっておらず、最後まで例4が合わなかった。


D - Let's Play Nim

問題
残り時間2分ほどで思いついて慌てて実装し、動作確認もできずに残り時間2秒で提出したコードが、ほんの些細な実装ミス1つで駄目だった。

思考過程
  1. 袋から皿に出し終わった時点をゲーム開始とした場合、各皿のコイン枚数の総xorが0ならば後手勝ち、以外は先手勝ちとなる。
     
  2. まず、袋が1つの場合、1 \leq a_iなので絶対に前半パート(袋から皿に出す部分)で総xorが0にはならないので、後半パートの先手が必勝。つまり後手必勝。
  3. 袋が2つの場合、後手は前半パートで0にしたいが、それは2つとも同じ枚数の場合のみ可能。
  4. とりあえずここまでは間違いなさそうだが、袋が3つ以上の場合はぱっとはよくわからない。
     
  5. a_iが全て1の場合で考えてみると、Nが偶数の場合は0にできて、後半パートが先手からなので後手勝ち、Nが奇数の場合は0にならず、後半パートが後手からなので後手勝ち。 0になることの方がとても少なそうなので、とりあえず3 \leq Nの場合は全部後手勝ちで提出してみる。 →WA
     
  6. Nが奇数の場合は、前半パートの最後から2手目次第で後手がだいたい0にならないようにできそうな気がする。 とりあえずNが奇数の場合は後手勝ち、偶数の場合は先手勝ちで提出してみる →WA
     
  7. 上記5.で、a_iが全部同じなら真似することで後手が0にできそうなんだった。 Nが偶数の場合について、1つでも異なる値があれば先手勝ち、それ以外は後手勝ちにしてみる。 →WA
     
  8. 真似プレイが成立するためには、全要素が同じ値ではなく、同じ値が偶数個ずつあればいいのではとなる。 →実装ミスでWA
  9. 解説を読んで合ってるじゃんとなり、見直したら実装ミスに気付いてAC。
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int t = Integer.parseInt(br.readLine());
        PrintWriter pw = new PrintWriter(System.out);
        label:
        for (int i = 0; i < t; i++) {
            int n = Integer.parseInt(br.readLine());
            String[] sa = br.readLine().split(" ");
            int[] a = new int[n];
            for (int j = 0; j < n; j++) {
                a[j] = Integer.parseInt(sa[j]);
            }
            // 思考過程2(思考過程の産物であり、ここはなくても通る)
            if (n == 1) {
                pw.println("Second");
                continue;
            }
            // 思考過程3(思考過程の産物であり、ここはなくても通る)
            if (n == 2) {
                if (a[0] == a[1]) {
                    pw.println("Second");
                } else {
                    pw.println("First");
                }
                continue;
            }
            if (n % 2 == 1) { // 思考過程6
                pw.println("Second");
            } else {
                Map<Integer, Integer> map = new HashMap<>();
                for (int j = 0; j < a.length; j++) {
                    map.put(a[j], map.getOrDefault(a[j], 0) + 1);
                }
                for (Integer val : map.values()) { // ここがmap.keySet()になっていた
                    if (val % 2 != 0) { // もしくはここでmap.get(val)にし忘れた
                        pw.println("First");
                        continue label;
                    }
                }
                pw.println("Second");
            }
        }
        pw.flush();
        br.close();
    }
}

 


終結果:ABの2完、19分16秒、1424位、パフォーマンス1069
レート変動:1796→1741(-55)


ここ1年間で最低の結果だが、最後にDが通っていれば1800は出ていたので惜しかった。
あと10秒あればあんな実装ミスはしなかったのではとも思うが、思考過程でわかる通り、D問題を自信持って解けているわけでもないので、あんまり言っても仕方ないところ。

とはいえ、こんな時にA問題も誤読していて、B問題も手間取っていて、最初で出遅れた時に自分のレート前後のdifficultyの問題が1問も解けず逆転できないのはなかなか辛い・・。
(先週のARC104はB問題まで似たような推移だったが、黄diffを通すという大逆転があった。)