nakamurk’s diary

日々思うことは残していきます。しっかり生きます。

私の解法(ABC194_Cについて)

ブログ(アウトプット)を再開するためにこちらを書きます。今回は、2021年3月6日(土)に実施されたAtCoder Begginer Contest194のC問題について(C - Squared Error*1、私の解法を解説します。

TL/DR

考え方

愚直な実装

Σの分だけ二重ループで解こうと思うと、次のようになると思います。

#include 
#define ll long long
using namespace std;

int main() {
  ll N, ret = 0; cin >> N;
  vector A(N+1); // 0オリジン(始まり)なので+1しています
  for (int i = 1; i <= N; i++) cin >> A[i];

  for (int i = 2; i <= N; i++) {
    for (int j = 1; j <= i - 1; j++) {
      ret += (A[i] - A[j]) * (A[i] - A[j]);
    }
  }
  cout << ret << endl;
  return 0;
}

しかし、これでは、入力例は問題なくても、次で詳細を説明するTLEが起こりそうな量の入力には耐えられません。

  • 入力例1

    $ IN=1.in; cat $IN; timeout -sKILL 2 bash -c "time ./a.out < $IN"
    3                // 入力
    2 8 4
    56               // 出力
    real    0m0.017s // 計算時間
    user    0m0.001s
    sys     0m0.005s
    

  • 入力例2

    $ IN=2.in; cat $IN; timeout -sKILL 2 bash -c "time ./a.out < $IN"
    5
    -5 8 9 -4 -3
    950
    real    0m0.017s
    user    0m0.000s
    sys     0m0.006s
    

  • 最大の入力例

    $ IN=stdin.; head $IN; timeout -sKILL 2 bash -c "time ./a.out < $IN"
    300000
    18
    72
    -144
    3
    10
    -57
    94
    106
    149
    Killed                          // TLE
    

私の解法

N=3で式を眺めてみます。簡単のためAiをabcと表すことにします。


\sum_{i=2}^3 \sum_{j=1}^{i-1} (A_i - A_j)^2  \\
= (b - a)^2 + (c - a)^2 + (c - b)^2  \\
= (b^2 -2ba + a^2) + (c^2 - 2ca + a^2) + (c^2 - 2cb + b^2) \\
= 2(a^2 + b^2 + c^2) - 2(ba + ca + cb)

同様にN=5で式を眺めてみますと、次のようになります。


\sum_{i=2}^5 \sum_{j=1}^{i-1} (A_i - A_j)^2  \\
= (b - a)^2 + (c - a)^2 + (c - b)^2 \\
 + (d - a)^2 + (d - b)^2 + (d - c)^2 \\
 + (e - a)^2 + (e - b)^2 + (e - c)^2 + (e - d)^2 \\
= (b^2 -2ba + a^2) + (c^2 - 2ca + a^2) + (c^2 - 2cb + b^2) \\
 + (d^2 - 2da + a^2) + (d^2 - 2db + b^2) + (d^2 - 2dc + c^2) \\
 + (e^2 - 2ea + a^2) + (e^2 - 2eb + b^2) + (e^2 - 2ec + c^2) + (e^2 - 2ed + d^2) \\
= 4(a^2 + b^2 + c^2 + d^2 + e^2) - 2(ab + ab + ad + ae + bc + bd + be + cd + ce + de)

ここで、それぞれの項の特徴に気が付きます。

  • 2乗の項
    • ①自身以外の項との組み合わせの数だけ項が存在する:N-1個
  • 各項の積
    • ②aからeの入った袋から2つ球を取り出すときの組み合わせの数

②に関して、安直に組み合わせを考えると、次のようにTLEを起こしそうです。


_N C _2 = \frac{300,000 x 299,999}2 = 44,999,850,000

ですので、計算方法でカバーします。第2項だけに着目すると次のように計算量を少なくできそうです。


ab + ab + ad + ae + bc + bd + be + cd + ce + de \\
= a(b + c + d + e) + b(c + d + e) + c(d + e) + de

これが私の実装の解説となります。変数名などが今一つですが、コード長は423 Byte、実行時間63 ms、メモリ5480 KBと、そこそこの実装だと思います。

テスト(タイムアウトをしないか試験する)

ABCのC問題は愚直に計算するとTLEが起きる問題が多いと思うので、ローカル環境でも、TLE が起きそうなケースを試しておくとよいと思います。

コマンドの準備(汎用的)

Linuxのコマンドにタイムアウトというものがあります。

$ timeout --version
timeout (GNU coreutils) 8.30

タイムアウトさせたい時間とその時のシグナルを設定できます。

# timeout -sKILL 2 ./a.out < stdin

また、実行時間も計測したいので、timeコマンドも挟んでみましょう。

$ timeout -sKILL 2 bash -c "time sleep 1"

real    0m1.003s
user    0m0.001s
sys     0m0.001s

sleep(実行時間を想定)がtimeoutを超えると次のようになります。

$ timeout -sKILL 2 bash -c "time sleep 3"
Killed

今回のテストデータの準備

試験本番ではテストデータの用意の仕方間違えました…。

$ echo 300000 > stdin; for i in `seq 1 300000`; do echo $i >> stdin; done
$ head stdin
300000
1
2
3
...

これでは制約事項「@|Ai|<=200@」が満たされていませんね。実際に入力してみると桁あふれしていることが分かります。

$ ./a.out < stdin
-7529530734753409792

ですが、実行時間だけを取ってみれば、間に合っていることが分かります。私はこの段階で、提出をしました(汗)。

$ timeout -sKILL 2 bash -c "time ./a.out < stdin"
-7529530734753409792

real    0m1.072s
user    0m0.236s
sys     0m0.079s

本来であれば、絶対値が200以下の自然数ですので、次のように与えるべきですね*2。そして、これでは完全なランダムではないですが一旦置いておくことにします。10秒ほどで、テストデータが作成できました(答えは不明です…)。

$ time (echo 300000; while true; do echo $(( ($RANDOM - 16383) % 200 )); done) | head -n 300001 > stdin

real    0m9.753s
user    0m7.171s
sys     0m10.019s

$ head stdin.
300000
18
72
-144
3
10
-57
94
106
149

実際に実行してみると、次のようになりました。1秒以内に結果が出力されているので問題なさそうですね。

$ wc -l stdin; timeout -sKILL 2 bash -c "time ./a.out < stdin"
300001 stdin
1187018545951196

real    0m0.701s
user    0m0.190s
sys     0m0.036s

蛇足

記事書くの大変ですね。この文章で5,000文字くらい(原稿用紙12枚、35ツイート分)で、2,3時間くらいかけました。丁寧な解説をされている先輩方には頭が上がりません。また、感想戦?組の実装は驚異的です。bashawkでの実装までありました。もはや脱帽というか、禿げそうです!それは脱毛w

https://m.media-amazon.com/images/I/41oruV+aJIL._SL75_.jpg https://m.media-amazon.com/images/I/51TtkYDXZFL._SL75_.jpg https://m.media-amazon.com/images/I/51hkUx5456L._SL75_.jpg