Official

D - 配送センターの稼働計画 / Operation Plan for Distribution Center Editorial by physics0523


まず、 \(N=1\) である場合を吟味しましょう。
\(f(x) = \{\) \(x\) 日目から \(x+K-1\) 日目まで配送を行った場合の合計不満度の最小値 \(\}\) とします。

関数 \(f(x)\) の振る舞いは以下の通りです。

  • \(x \le D_1-K\) のとき、 \(f(x)\)\(x\)\(1\) 増えると \(1\) 減少する。
  • \(D_1-K+1 \le x \le D_1-1\) のとき、 \(f(x)\)\(x\)\(1\) 増えても同じ値をとる。
  • \(D_1 \le x\) のとき、 \(f(x)\)\(x\)\(1\) 増えると \(1\) 増加する。

\(f(x)\) の傾きは \(-1,0,1\) と変動します。

\(N \ge 2\) の場合、当然ながら合計の不満度はこれらの関数をいくつか足し合わせたものになります。このとき、 \(f(x)\) の傾きはどうなるでしょうか?

\(f(x)\) の傾きは \(-N,-(N-1),\dots,-1,0,1,\dots,(N-1),N\) と変化することがわかります。(場合によっては一部の傾きが消滅しますが、 \(x\) を増やすにつれて傾きが増加する性質は変わりません)
このことから、以下の二分探索が成立します。

  • \(f(t),f(t+1)\) を求める。
  • \(f(t) > f(t+1)\) であれば、調べるべきは \(t\) より大きい領域である。
  • そうでなければ、調べるべきは \(t\) より小さい領域である。

上記で排除した領域は最小値たりえないことが、傾きの変化から分かります。

計算量は \(O(N \log D_i)\) となります。

実装例 (C++):

#include<bits/stdc++.h>

using namespace std;
using ll=long long;

ll N,K;
vector<ll> D;

ll f(ll l){
  ll r=l+K-1;
  ll res=0;
  for(auto &nx : D){
    if(nx<l){res+=(l-nx);}
    else if(r<nx){res+=(nx-r);}
  }
  return res;
}

int main(){
  cin >> N >> K;
  D.resize(N);
  for(auto &nx : D){cin >> nx;}
  ll lef=1,rig=1.1e9;
  ll res=8e18;
  while(lef<=rig){
    ll te=(lef+rig)/2;
    ll f1=f(te);
    ll f2=f(te+1);
    res=min({res,f1,f2});
    if(f1>f2){lef=te+1;}
    else{rig=te-1;}
  }
  cout << res << "\n";
  return 0;
}

posted:
last update: