絶対値ぽまえ絶対許さん

codeFlyer (bitFlyer Programming Contest)オープンコンテスト B - 交通費 

 

まず、問題文を読んで入力を作ります。

制約をよく読むと 

Xi<Xi+1 (1i<N) 

なので、すでにソートされていることが分かります。制約的にn^2だと間に合わないですし、二分探索を使ってくださいと言っている気がするので、二分探索を使いましょう。

使いどころとしては

X:1 5 10 20 30 、c=7、d=3 

を例にとると、

|1-7| > 3    |20-7|  > 3 

なのでこの時はd円でよいことが分かります。

つまり、計算すればいいところは

初めて|Xn - C| <= d となるところから |Xn - C| > d となるところまで

ということが分かります。ここを二分探索で調べます。

しかし、このままではまだ不十分です。よく考えると

X:590 593 633 642 743 859 872 、c=642,d=850108511

のときのようにdがクソデカだと、どうしても すべてのXnについて

|Xn - C| <= d   になってしまい、計算量がn^2になってしまいます。

ここで、絶対値の計算する必要ある?と気が付ければこの問題は解けます(僕は気が付けませんでした。つらいね)

はい。絶対値で表記されていますが、別に絶対値のまま計算する必要はないんですね。

 

というのも、

Xn  <=  C の時、 |Xn - C| =  C - Xn  (例 |16 - 64| =  64 - 16 = 48) 

Xn  >=  C の時、 |Xn - C| =   Xn - C    (例 |334 - 57| = 334 - 57 =  277)

となるからです。そうすると、

初めて|Xn - C| <= d となるところから |Xn - C| > d となるところまで 

を部分数列Yとすると、

C*(項の数) - 数列YのC以下の数の和  + 数列YのC以上の数の和- C*(項の数)

(それ以外の項)*dを足せば求められそうです。累積和を使いましょう。

 

X:590 593 633 642 743 859 872,  c=633,d = 20 を例にとると、

 

  |Xn - C | > d             |Xn - C|<= d         |Xn - C | > d 

 590  593          <-|      633   642   |->  743  859  872

         |     部分数列Y       |

 

このような構成になっていることがわかるので、

数列YのC以下の数の和 の求め方を考えるとC以下の項までの和から( | Xn - C | <= d )

になるまでの和を引くと求められそうです。

項の数も、C以下の項の数 - ( | Xn - C | <= d )になるまでの項の数 で求められます。

数列YのC以上の数の和 も同様に |Xn - C| > d になるまでの和 - C以下の和 で求められて、項の数も同じようにして求められます。

それ以外の項 数列Xの項の数 - 部分数列Yの項の数 で求められることが分かります

これまた二分探索でC以下の項で一番大きいものを探してやればいいです。

これでこの問題は解決です。