题目链接
算是巩固了一下斜率优化吧。
设(f[i])表示前(i)分钟最少等待时间。
则有(f[i]=min_{j=0}^{i-m}f[j]+(cnt[i]-cnt[j])*i-(sum[i]-sum[j]))
其中(cnt[i])和(sum[i])分别表示前(i)分钟去等车的学生数量和他们去等车的时刻之和。
变形一下得(f[j]+sum[j]=i*cnt[j]+i*cnt[i]-sum[i]-f[i])
维护一个下凸包即可。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 510;
const int MAXT = 4000010;
int n, m, t[MAXN], cnt[MAXT], sum[MAXT], f[MAXT], ans = 2147483647, q[MAXT], head, tail;
inline double k(int i, int j){
return (double)(f[j] + sum[j] - f[i] - sum[i]) / (cnt[i] == cnt[j] ? 1e-9 : cnt[j] - cnt[i]);
}
int main(){
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i){
scanf("%d", &t[i]);
cnt[t[i]]++;
sum[t[i]] += t[i];
}
sort(t + 1, t + n + 1);
for(int i = 0; i < t[n] + m; ++i)
cnt[i] += cnt[i - 1], sum[i] += sum[i - 1];
for(int i = 0; i < m; ++i)
f[i] = cnt[i] * i - sum[i];
for(int i = m; i < t[n] + m; ++i){
while(head < tail && k(q[tail - 1], q[tail]) >= k(q[tail], i - m)) --tail;
q[++tail] = i - m;
while(head < tail && k(q[head], q[head + 1]) <= i) ++head;
int j = q[head];
f[i] = f[j] + (cnt[i] - cnt[j]) * i - (sum[i] - sum[j]);
if(i >= t[n]) ans = min(ans, f[i]);
}
printf("%d
", ans);
return 0;
}