AtCoder Regular Contest 145

コンテスト前のレート:2026
レート通りのパフォーマンスが得られる順位:304位(1500点、63分59秒)

A - AB Palindrome

問題
下記1.までで11分、3.までで20分かかっていて出だしからボロボロ。

思考過程
  1. 両端から見ていって、左側が'A'かつ右側が'B'ならアウト、左側が'B'かつ右側が'A'なら操作を行って合わせていく感じ? →4割しか合っていない
     
  2. そもそも両端以外はだいたい任意の文字列にできそうな気がしてくる。
  3. 'A'から始まって'B'で終わる以外は全部可能そう。 →1ケースWA
  4. コーナーケースを考えてみると、他にあと"BA"だけ駄目なことに気付く。
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());
        char[] s = br.readLine().toCharArray();
        br.close();

        if (s[0] == 'A' && s[n - 1] == 'B' ||
                n == 2 && s[0] == 'B' && s[1] == 'A') {
            System.out.println("No");
        } else {
            System.out.println("Yes");
        }
    }
}

ここまで24分41秒+2ペナ。1034位。


B - AB Game

問題

思考過程
  1. A \leq Bならば、最初にAliceが取れるだけ取れば余りはA未満であり、B未満でもあるので必ず勝てる。
  2. N \lt Aの場合だけ一度も取れずに負けるので、その分は引いておく。
     
  3. A \gt Bの場合は、A=5, B=3とかで実験してみると、N=5, 6, 7, 10, 11, 12, 15, 16, 17, \cdotsの時に勝てる。
  4. 取れるだけ取った余りがB未満なら勝てる模様。
  5. また、上記1.の考察により、取れるだけ取らないことで勝てるようになることもない。
     
  6. 勝ちと負けの並びはA周期で繰り返すので、答えはおおよそB \times \frac{N}{A}になるが、最後の1周期分は余りが出る可能性があるため、余りとBのminを取る。
     
  7. WAが出たがすぐに原因がわかりそうにないので、提出デバッグをして駄目なところを絞り込む。
  8. A \gt Bの中でREを発生させてみると、ACは減らなかったため、間違っていたのはなんとA \leq Bの方。
  9. とりあえずちゃんと動かしてみると、答えがマイナスになったりしている。
  10. 入力がN \lt Aの場合に0としていなかった。
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));
        String[] sa = br.readLine().split(" ");
        long n = Long.parseLong(sa[0]);
        long a = Long.parseLong(sa[1]);
        long b = Long.parseLong(sa[2]);
        br.close();

        if (n < a) {
            System.out.println(0);
        } else {
            if (a <= b) {
                System.out.println(n - a + 1);
            } else {
                long x = n / a;
                long v1 = b * (x - 1);
                long y = n % a + 1;
                long v2 = Math.min(y, b);
                System.out.println(v1 + v2);
            }
        }
    }
}

ここまで39分42秒+4ペナ。607位。



Cは解けず。
とりあえず1 \times 2 + 3 \times 4 + \cdotsを作ればよいことはすぐわかったが、それを重複なく数える方法が全然わからず。
括弧列に対応させるというのも全然出てこなかった。

経過時間70分くらいからDも考え始め、90分経過する頃にはもうCが500~600人に通されていたのでDに賭けることにする。


D - Non Arithmetic Progression Set

問題
いつの間にかブラウザ右下に表示されている時刻が実際の時刻より20秒ほど進んでいて、まだ時間が残っているのに諦めそうになった。

思考過程
  1. とりあえず合計がMの条件は一旦忘れて、最小の要素を0として任意の3要素が等差数列になっていない集合の作り方を考える。
  2. これが作れれば、あとは程よく各要素から同じ値を足し引きし、最後の1つだけ極端な値にして合計を合わせることでできそう。
     
  3. しばらく手作業をしていたが埒が明かないので、可能な最小の値を集合に入れていく貪欲を書いてみる。
  4. 単純な貪欲では、最大の要素をXとするとO(XN)かかり、N=10^4では10秒ほどかかってX=1679656を求められた。
  5. 数列を全部出力してみると80000文字には収まるくらいだったので、埋め込めるか?と思ったが、少なくとも自分の環境では65536バイト以上だとエラーになってしまい断念。もしかしたらちょっと分割すればいけたかもしれないが。
     
  6. 貪欲の出力結果は1, 3, 4, 9, 10, 12, 13, 27, 28, 30, 31, 36, 37, 39, 40, 81, 82, \cdotsのようになっており、手作業の時から薄々気付いていたが、1, 2, 4, 8, \cdots個ごとに一気に2倍になっている形。
  7. それなら、1の次は2から調べる、4の次は8から調べる、13の次は26から調べる、\cdotsのようにしてやれば、無駄がほとんどなくなってO(N^2)になるのではないか。
  8. 手元ではN=10^4でもなんとか2秒には収まっていそう。
     
  9. あとは上記2.の部分を具体的にどうするかだが、貪欲の結果より、最後の要素に2X(余裕をもって4Xとしておく)以上を足せば非等差数列の条件は守られるはず。
  10. よって、合計がだいたいM-4X程度になるように調整し、最後の要素にMから実際の合計を引いた値を加算する。
     
  11. 調整値を足すか引くかを実装ミスしていてサンプル以外全てWA。
  12. 符号を直しつつ、残り時間を確認しながらゆっくり動作確認していたら急にコンテスト終了のお知らせが出てくる。
  13. でも自分の時計ではまだあと20秒くらいあるはず?と思って一応慌てて提出してみたら、119:45でACとなった。
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.HashSet;
import java.util.Set;

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] sa = br.readLine().split(" ");
        int n = Integer.parseInt(sa[0]);
        long m = Long.parseLong(sa[1]);
        br.close();

        int[] a = new int[n];
        Set<Integer> set = new HashSet<>();
        set.add(0);
        int x = 1;
        int next = 1;
        int cnt = 0;
        // 3
        for (int i = 1; i < n; i++) {
            while (true) {
                boolean flg = true;
                for (int j = i - 1; j >= 0; j--) {
                    int val = a[j] - (x - a[j]);
                    if (val < 0) {
                        break;
                    }
                    if (set.contains(val)) {
                        flg = false;
                        break;
                    }
                }
                if (flg) {
                    a[i] = x;
                    set.add(a[i]);
                    break;
                }
                x++;
            }
            // 7
            cnt++;
            if (cnt == next) {
                x *= 2;
                cnt = 0;
                next *= 2;
            }
        }
        long sum = 0;
        for (int i = 0; i < n; i++) {
            sum += a[i];
        }
        // 4, 5
//      System.out.println(Arrays.toString(a));
//      System.out.println(sum);

        // 10
        long g = m - x * 4;
        long g2 = (g - sum + n - 1) / n;
        long sum2 = 0;
        for (int i = 0; i < n; i++) {
            a[i] += g2; // 11
            sum2 += a[i];
        }
        long add = m - sum2;
        a[n - 1] += add;

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < n; i++) {
            sb.append(a[i]).append(' ');
        }
        sb.deleteCharAt(sb.length() - 1);
        System.out.println(sb.toString());
    }
}

ここまで119分45秒+5ペナ。223位。



EとFは問題を開いてもいない。



終結果:ABDの3完1600点、144分45秒、224位、パフォーマンス2262
レート変動:2026→2052(+26)


2完59分42秒で終わっていたらパフォは1430ほどだったので、今回は立ち回りでパフォ800を稼いだ感じ。
1600点の人はたった14人しかおらず、時間が全然関係なくなったのもおいしかった。

カタラン数とか全然頭に入ってなかったし、自分はやっぱり知識寄りの問題に弱くて構築で時々大当たりがあるタイプなんだなと思う。