题解 LGP5017【[NOIP2018 普及组] 摆渡车】
problem
有 \(n\) 名同学要乘坐摆渡车从人大附中前往人民大学,第 \(i\) 位同学在第 \(t_i\) 分钟去 等车。只有一辆摆渡车在工作,但摆渡车容量可以视为无限大。摆渡车从人大附中出发、 把车上的同学送到人民大学、再回到人大附中(去接其他同学),这样往返一趟总共花费 \(m\) 分钟(同学上下车时间忽略不计)。摆渡车要将所有同学都送到人民大学。
凯凯很好奇,如果他能任意安排摆渡车出发的时间,那么这些同学的等车时间之和最小为多少呢?
注意:摆渡车回到人大附中后可以即刻出发。
对于 \(100\%\) 的数据,\(n ≤ 500, m ≤ 100, 0 ≤ t_i ≤ 4 \times 10^6\)。
solution 1
暴力不讲。
考虑 \(f_t\),表示 \(t\) 时刻有摆渡车出发,的最小等待时间。考虑如果有个 \(t\) 我们是能知道当前哪些人被送走的。
其中 \(s_i\) 是一个桶的前缀和,表示 \([1,i]\) 时间段有多少人在等车。\(t_i\) 是这些人等车时间的前缀和,拍到时间轴上。
有同学从 \(0\) 出发,要注意;最终的答案是后 \(m\) 个的最小值。
斜率为 \(s_j\),截距为 \(-f_j-t_j\),考虑李超线段树即可。
solution 2
Let \(y=f_j+t_j,k=i,x=s_j,b=f_i-is_i+t_i\), \(\therefore y=kx+b\).
将 \((x,y)\) 看作一个点,然后就是一条斜率为 \(k\) 的直线,从下往上扫到第一个点 \((x,y)\),这时这条直线的截距就最小,我们就胜利了。
维护一个凸包,然后发现 \(x,k\) 都单调。然后斜率优化就是了。
下面讲一下细节:
- 我们会发现存在大量的点的 \(x,y\) 坐标相同。
- 在入队和出队时我们要特判 \(x\) 坐标相同的,保留 \(y\) 较小的。具体看代码。
- 由此引出的另一种做法是:只保留关键点。
code
调试方法:
- 先打暴力。
- 然后算一下每个点的 \(x,y\) 坐标,用暴力看一下是不是优秀的。
- 输出队列中两个点之间的斜率,应该单调。
点击查看代码
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr,##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
typedef long long LL;
int q[4000010],n,m,maxk;
LL s[4000010],t[4000010],f[4000010];
LL dp(){
f[0]=0;
auto getx=[&](int i){return s[i];};
auto gety=[&](int i){return f[i]+t[i];};
auto getdx=[&](int i,int j){return getx(i)-getx(j);};
auto getdy=[&](int i,int j){return gety(i)-gety(j);};
auto slope=[&](int i,int j)->double{return 1.*getdy(i,j)/getdx(i,j);};
int L=1,R=0; q[++R]=0;
auto insert=[&](int i){
while(L<R&&(getdx(q[R],i)?slope(q[R-1],q[R])>=slope(q[R],i):getdy(q[R],i)>=0)) R--;//相同比较 y 坐标
// while(L<R&&(__int128)(gety(q[R-1])-gety(q[R]))*(getx(q[R])-getx(i))>(__int128)(gety(q[R])-gety(i))*(getx(q[R-1])-getx(q[R]))) R--;
if(getdx(q[R],i)) q[++R]=i;
else if(getdy(q[R],i)>=0) q[R]=i;//入队也不要乱入
};
auto cut=[&](int k){
while(L<R&&slope(q[L],q[L+1])<k) L++;//切凸包的时候已经没有相同的 x 了,正常切即可
// while(L<R&&(gety(q[L])-gety(q[L+1]))<k*(getx(q[L])-getx(q[L+1])));
return q[L];
};
for(int i=1;i<=maxk;i++){
debug("i=%d\n",i);
f[i]=i*s[i]-t[i];
if(i-m>=1) insert(i-m);
int j=cut(i);
// for(int l=L;l<=R;l++){int j=q[l];
f[i]=min(f[i],f[j]+i*(s[i]-s[j])-t[i]+t[j]);
debug("j=%d, f[i].update(%lld), at (%d,%d)\n",j,f[j]+i*(s[i]-s[j])-t[i]+t[j],getx(j),gety(j));
// }
// debug("f[%d]=%lld (trans from j=%d)\n",i,f[i],j);
for(int j=L;j<=R;j++) debug("%d,",q[j]);
debug("\n");
// for(int j=L;j<R;j++) debug("%.3lf,",slope(j,j+1));
// debug("\n");
// debug("slope(%d,%d)=%.3lf\n",i-1,i,slope(i-1,i));
}
return *min_element(f+maxk-m,f+maxk+1);
}
int main(){
// #ifdef LOCAL
// freopen("input.in","r",stdin);
// #endif
scanf("%d%d",&n,&m);
for(int i=1,x;i<=n;i++) scanf("%d",&x),x++,s[x]++,t[x]+=x,maxk=max(maxk,x+m);
for(int i=1;i<=maxk;i++) s[i]+=s[i-1],t[i]+=t[i-1];//,debug("s[%d]=%lld,t[%d]=%lld\n",i,s[i],i,t[i]);
printf("%lld\n",dp());
return 0;
}
本文来自博客园,作者:caijianhong,转载请注明原文链接:https://www.cnblogs.com/caijianhong/p/solution-P5017.html