AtCoder Regular Contest 107

コンテスト前のレート:1722
レート通りのパフォーマンスが得られる順位:631位(1200点、35分13秒)

A - Simple Math

問題
これが一瞬でわからない辺り、単純な数学はどちらかと言えば自分の弱点だと思う。

思考過程
  1. 適当に(1,1,1), (1,1,2), \cdotsと書き出してみたら、\sum_{a=1}^A \sum_{b=1}^B ab \times \sum_{c=1}^C cとまとめられた。
  2. ということは結局、\sum_{a=1}^A a \times \sum_{b=1}^B b \times \sum_{c=1}^C cとなり、\frac{A(A+1)}{2} \times \frac{B(B+1)}{2} \times \frac{C(C+1)}{2}を求めればよさそう。

ちょっと計算するし、3回並べるよりfor文にするかと書き換えたけど、何十秒か余計に時間かかっただけだったかもしれない。

import java.util.Scanner;

public class Main {
    public static void main(String[] args) throws Exception {
        Scanner sc = new Scanner(System.in);
        long[] a = new long[3];
        a[0] = sc.nextLong();
        a[1] = sc.nextLong();
        a[2] = sc.nextLong();
        sc.close();

        long ans = 1;
        for (int i = 0; i < 3; i++) {
            ans *= a[i] * (a[i] + 1) / 2 % 998244353;
            ans %= 998244353;
        }
        System.out.println(ans);
    }
}

ここまで4分51秒+0ペナ。1134位。


B - Quadruple

問題

思考過程
  1. (a+b)-(c+d)=Kと変形してみると、適宜(a+b)(c+d)を入れ替えればので、Kは絶対値を取って0以上の場合のみ考えればよい。
  2. i=(a+b)となるiを作る方法が何通りあるかを求める。二次元表を書いて実験してみると、以下のように山なりになっている。サイコロを2個振ったときのことを思い浮かべたら確かにこんな感じ。
    • i=2の場合1通り
    • i=3の場合2通り
    • \cdots
    • i=N+1の場合N通り
    • \cdots
    • i=2Nの場合1通り
  3. とりあえずこれを求める関数を作る。前半の増える部分はi-1で、後半の減る部分は-i+xxを適当にガチャガチャやって書き出した二次元表と合わせるようにして求める。
     
  4. 最初の式を(a+b)-K=(c+d)と変形すると、各iについて、上記3の関数を引数ii-Kで呼び出し、それぞれの結果の積を答えに足していく。
import java.util.Scanner;

public class Main {
    public static void main(String[] args) throws Exception {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int k = sc.nextInt();
        sc.close();

        k = Math.abs(k);
        int n2 = n * 2;
        long ans = 0;
        for (int i = 2; i <= n2; i++) {
            if (i < k + 1) { // 最初i < kとしており、デバッグで数分ロス
                continue;
            }
            long val1 = cnt(n, i);
            long val2 = cnt(n, i - k);
            ans += val1 * val2;
        }
        System.out.println(ans);
    }

    static long cnt(int n, int i) {
        long val = i - 1;
        if (i > n + 1) {
            val = n + n + 1 - i;
        }
        return val;
    }
}

ここまで18分48秒+0ペナ。756位。


C - Shuffle Permutation

問題
解けず。これが緑diffってどういうことですか。

思考過程
  1. とりあえず3 \times 3のマスに1~9を入れて、合計K以下の制限はないものとして、与えられている操作を適当に行ってみる。
  2. すると、各行内、列内の数字の順番は変わるが、内訳は変わらないことがわかる。
  3. Kの制限がない場合、N=3の場合は、縦横それぞれ任意の順列にすることができ、(3!)^2=36となりそう。例1では、行の並べ替えをしようとしたときに、1, 3行目の入れ替えしかできないため、(3!) \times 2=12となっている。
     
  4. 例1の列の並べ替えについては、1,2列目、1,3列目のみ入れ替え可能だが、1列目を介すことで実質2,3列目の入れ替えも可能になっている。
  5. ここでUnionFindっぽさを感じはしたが、少なくとも特定の1行(列)について見たときは、その中で最小の要素との和がK以下である要素は全て、最小の要素と同一連結成分にできることになり、また、連結成分は必ず1つになるはず?これがN行全体でも同様だと思い込む。
  6. 長さNのboolean型配列を用意し、初め全てtrueで初期化する。各行について、最小要素との和がKより大きい列をfalseにしていく。N行分処理後、trueで残った要素数の階乗が答え。
     
  7. 行列入れ替えて求め、2つの結果の積が最終的な答え。 →9WA
  8. 添え字ミスがあったので直す。 →7WA
  9. ベルマンフォード法と似たイメージで、N回調べないとfalseなのにfalseにならない箇所が出るかも。 →7WA

結局、解説を見た後も、連結成分が複数できるパターンが存在し得るのかよくわかっていない。



D問題も20~30分ほどは考えたけど解けず。
ABCDの4完でもABDの3完でもパフォはほとんど変わらなそうだったので、もっと死ぬ気でDをやればよかったのかもしれない。
実装量はCよりDの方がはるかに少なかった。
E、Fは正解者数を見て絶対無理だと思い、問題を開いてもいない。



終結果:ABの2完、18分48秒、1494位、パフォーマンス1163
レート変動:1722→1676(-46)


約半年ぶりに1700台を割り込んだ。highest-144まで下がるという悲惨な状態。
もう水色まで落ちなければ何でもいいやという感じ。