AtCoder Regular Contest 130

コンテスト前のレート:1984
レート通りのパフォーマンスが得られる順位:302位(1200点、85分53秒)

A - Remove One Character

問題

思考過程
  1. 同じ文字がK個連続する区間ごとに、_KC_2を足していきたい。
  2. これは、同じ文字がK個目であるごとにK-1を足していっても同じなので(K個目の文字をjとした時の相方となるiK-1通りのため)、そのように実装する。
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();

        long ans = 0;
        long cnt = 0;
        for (int i = 1; i < n; i++) {
            if (s[i - 1] == s[i]) {
                cnt++;
                ans += cnt;
            } else {
                cnt = 0;
            }
        }
        System.out.println(ans);
    }
}

ここまで1分58秒+0ペナ。39位。


B - Colorful Lines

問題

思考過程
  1. 普通に前からやって上書き部分を正しく処理できるか、もしくは後ろからやって一度塗られた部分は避けることを上手く処理できるか。
  2. 以下のようにすれば後ろからできる。
  3. ある行を塗ったら、その行は二度と塗れないとし(塗った行番号をSetで管理する)、次回列方向に塗る際に塗れるマスの数を1減らす(H1減らす)。 列を塗る場合も同様。
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 h = Integer.parseInt(sa[0]);
        int w = Integer.parseInt(sa[1]);
        int m = Integer.parseInt(sa[2]);
        int q = Integer.parseInt(sa[3]);
        int[] t = new int[q];
        int[] n = new int[q];
        int[] c = new int[q];
        for (int i = 0; i < q; i++) {
            sa = br.readLine().split(" ");
            t[i] = Integer.parseInt(sa[0]);
            n[i] = Integer.parseInt(sa[1]);
            c[i] = Integer.parseInt(sa[2]) - 1;
        }
        br.close();

        long[] ans = new long[m];
        Set<Integer> x = new HashSet<>();
        Set<Integer> y = new HashSet<>();
        for (int i = q - 1; i >= 0; i--) {
            if (t[i] == 1) {
                if (x.add(n[i])) {
                    ans[c[i]] += w;
                    h--;
                }
            } else {
                if (y.add(n[i])) {
                    ans[c[i]] += h;
                    w--;
                }
            }
        }

        StringBuilder sb = new StringBuilder();
        for (long i : ans) {
            sb.append(i).append(' ');
        }
        sb.deleteCharAt(sb.length() - 1);
        System.out.println(sb.toString());
    }
}

ここまで8分13秒+0ペナ。80位。


C - Digit Sum Minimization

問題
下記の方法で穴がないのかはあまり自信ない。
たまたま例2で引っかかったおかげでなんとかなった。

思考過程
  1. 繰り上がりがなければ、どのように並べ替えようとも合計は変わらず、逆に言えば繰り上がる度に減っていくので、繰り上がり回数を最大化する問題。
     
  2. a_i+b_i+k \geq 10kは繰り上がりがあれば1、なければ0)となる組合せをなるべく多くするには、下の桁から順に、合計1019の優先順で作ればよい?
  3. 合計1019のいずれも作れなくなったら、合計29を適当に作り、a, bの短い方の桁数を過ぎた後は、繰り上がりが維持されている可能性も考慮して余ったものを91の順に並べることにする。
  4. これだけだと嘘くさいとは思ったがやはり例2が駄目で、一の位が4+6になってしまい、十の位で3+6+1が作れなくなるため、一の位を5+5にできる方法を考える。
     
  5. 一の位は、十の位以降でa_i+b_i=9を作れる個数が減らないように決める方法があるならば、そのようにする必要がある。
  6. a, bに含まれる19の個数から、(1, 8), (2, 7), \cdots, (8, 1)の組合せを作れる個数を一旦引いた上で合計1019が作れるかどうかを試すようにする。
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));
        char[] a = br.readLine().toCharArray();
        char[] b = br.readLine().toCharArray();
        br.close();

        int[] x = new int[10];
        for (int i = 0; i < a.length; i++) {
            x[a[i] - '0']++;
        }
        int[] y = new int[10];
        for (int i = 0; i < b.length; i++) {
            y[b[i] - '0']++;
        }

        // (1, 8)、(2, 7)、・・・分を一旦引く
        int[] q = new int[10];
        for (int i = 1; i < 9; i++) {
            q[i] = Math.min(x[i], y[9 - i]);
            x[i] -= q[i];
            y[9 - i] -= q[i];
        }

        int min = Math.min(a.length, b.length);
        StringBuilder sa = new StringBuilder();
        StringBuilder sb = new StringBuilder();
        int k = 0;
        int s = 0;
        // 一の位
        label:
        for (int j = 10; j < 20; j++) {
            for (int j1 = 1; j1 < 10; j1++) {
                int j2 = j - j1 - k;
                if (j2 < 10 && x[j1] > 0 && y[j2] > 0) {
                    sa.append(j1);
                    sb.append(j2);
                    x[j1]--;
                    y[j2]--;
                    k = 1;
                    s = 1;
                    break label;
                }
            }
        }

        // 引いた分を戻す
        for (int i = 1; i < 9; i++) {
            x[i] += q[i];
            y[9 - i] += q[i];
        }

        label:
        for (int i = s; i < min; i++) {
            for (int j = 10; j < 20; j++) {
                for (int j1 = 1; j1 < 10; j1++) {
                    int j2 = j - j1 - k;
                    if (j2 < 10 && x[j1] > 0 && y[j2] > 0) {
                        sa.append(j1);
                        sb.append(j2);
                        x[j1]--;
                        y[j2]--;
                        k = 1;
                        continue label;
                    }
                }
            }
            for (int j = 2; j < 10; j++) {
                for (int j1 = 1; j1 < j; j1++) {
                    int j2 = j - j1 - k;
                    if (j2 < 10 && x[j1] > 0 && y[j2] > 0) {
                        sa.append(j1);
                        sb.append(j2);
                        x[j1]--;
                        y[j2]--;
                        k = 0;
                        continue label;
                    }
                }
            }
        }

        // aとbの桁数が異なる場合の余り分の処理
        int[] z = x;
        StringBuilder sb2 = sa;
        if (b.length > min) {
            z = y;
            sb2 = sb;
        }
        for (int i = 9; i > 0; i--) {
            for (int j = 0; j < z[i]; j++) {
                sb2.append(i);
            }
        }

        sa.reverse();
        sb.reverse();
        System.out.println(sa.toString());
        System.out.println(sb.toString());
    }
}

ここまで33分15秒+0ペナ。29位。



残り時間の大半はEに使った。
全部可能な最小値から始めるようにしたら不可能判定ができておらず、不可能になったら+1から始めるのがO(K^2)しか思い付かず。

素直にDにもっと時間を使えばよかったかもしれないが、根からDPすることしか考えていなくて、一直線でないとできる気がしなかった。

ラスト15~20分程度でFにも特攻してみて、最終形がどうなるのかの仮説まではだいたい立ったのだが、あくまでだいたい合ってるレベルで、正確には詰め切れていなかったのでやはり厳しかった。



終結果:ABCの3完1200点、33分15秒、137位、パフォーマンス2363
レート変動:1984→2028(+44)


どこまで落ちていくかと思ったが、青落ち3回目もあっさり黄色復帰することができ、とりあえずまだ黄色でいさせてもらえるらしい。