AtCoder Beginner Contest 102 C - Linear Approximation
問題
考察と解法
今回は結構考察パートが長めです。
問題としては以下の関数の値を最小化するbを求め、それを代入したの値を求めるといったものです。 は入力として与えられるのでbの値だけを考えれば良いです。
まず、(-1, -2, ..., -N)が定数になるため、これを事実上の定数であると合わせて、式変形を行います。
ここでとすると
となります。これは、全てのとbとの距離の総和を意味しています。bは自明にになりますが、の取る値がからであるため、いつも通り全探索では時間内に計算が終わりません。おとなしく問題と向き合いましょう。
また、ここで考えるのを簡単にするために、の各要素を昇順(小さい順)に並べ替えたものをとします。
これはもちろん
が成り立つため、全てのとbの距離の総和について考えることにします。
ここで、あるをだけ増加させた場合、は以下のようになります。
したがって、bをjだけ増加させた場合の関数の増加量は
(増加式)
となります。を最小化するためには、上式を最小化すればよいです。
右辺に着目すると
上の式が最小となるのは以下の2パターンです。
- かつ
- かつ
言い換えると、jの値を
- のとき
- のとき
とすることで関数の増加量を最小化できます。また、最小化した場合、(増加式)が0以下の値を取るため、が成り立ち、の最小値はとなります。
そして、の最小値、つまりの最小値は
- のとき
- のとき
となります。
また、の最小値は自明に全てのに対するの最小値であることから、
であり、はによっての適切な方を選べばよいことがわかったため、
となります。あとは、各についてを求める方法がわかればこの問題を解く事ができます。
ここで、の式を確認すると
より
Bが昇順に並んでいるという制約から
- なら
- なら
よって、
(一般式)
となります。とは予め累積和を用いて計算しておく事で各に対してで求める事ができます。
以上の(一般式)を全てのに対して計算した結果の最小値がこの問題の解となります。
計算量
- 各要素をソートするのに
- 累積和を計算するのに
- 全てのに対して(一般式)を計算するのに
1, 2, 3の操作は互いに独立しているため、全体での時間計算量はになります。
実装の注意点
この問題では、(一般式)が取りうる最大値を取った時に大体となるため、32ビットで表現できる数値の範囲を超えてしまいます。
したがって、殆どの環境で32ビットとなるint型
ではオーバーフローが発生します。C++ではlong long型
が64ビットの幅を持っているため、これを利用しましょう。
ソースコード
#include <iostream> #include <algorithm> #define int long long using namespace std; signed main() { int n; int a[200000]; int sum[200001] = { 0 }; cin >> n; for(int i = 0; i < n; ++i) { cin >> a[i]; // 予めAをA'に変換しておく a[i] -= i + 1; } // A'からBへの変換 sort(a, a + n); // 累積和を計算[) for(int i = 1; i <= n; ++i) { sum[i] = sum[i - 1] + a[i - 1]; } // f(B, b)の最小値 int d = 1LL << 60LL; for(int i = 0; i < n; ++i) { int b = a[i]; int leftD = (i * b) - (sum[i]); int rightD = (sum[n] - sum[i + 1]) - (n - 1 - i) * b; d = min(d, leftD + rightD); } cout << d << endl; }
感想
300点の問題にしては少し難しいと感じました。bの値の候補をいかに絞り込む事ができるかがこの問題の鍵でした。