LG P5017 [NOIP2018]摆渡车
题目描述
有\(n\)名同学要乘坐摆渡车从人大附中前往人民大学,第\(i\) 位同学在第\(t_i\) 分钟去 等车。只有一辆摆渡车在工作,但摆渡车容量可以视为无限大。摆渡车从人大附中出发、 把车上的同学送到人民大学、再回到人大附中(去接其他同学),这样往返一趟总共花费\(m\)分钟(同学上下车时间忽略不计)。摆渡车要将所有同学都送到人民大学。凯凯很好奇,如果他能任意安排摆渡车出发的时间,那么这些同学的等车时间之和最小为多少呢? 注意:摆渡车回到人大附中后可以即刻出发。
分析
设\(f_i\)表示在时段\([0,i]\)内用摆渡车接送,且最后一次接送的终止时间为\(i\)时的时间最小值。枚举上一次的端点\(j\),注意到\(j\in [0,i-m]\),有
\[f_i\leftarrow\min\left\{\sum_{t_k\le i}(i-t_k),\min_{j\le i-m}\left\{f_j+\sum_{j<t_k\le i}(i-t_k)\right\}\right\},\ i\in[0,t+m)\bigcup\mathbb Z
\]
其中\(t=\max_{1\le k\le n}t_k\)。
注意到
\[\sum_{j<t_k\le i}(i-t_k)=\sum_{j<t_k\le i}i-\sum_{j<t_k\le i}t_k=i\cdot (\mathrm{cnt}_i-\mathrm{cnt}_j)-\mathrm{sum}_i+\mathrm{sum}_j\\
\sum_{t_k\le i}(i-t_k)=\sum_{t_k\le i}i-\sum_{t_k\le i}t_k=i\cdot \mathrm{cnt}_i-\mathrm{sum}_i
\]
其中\(\mathrm{cnt}_i\)为时段\([0,i]\)内点的个数,\(\mathrm{sum}_i\)为时段\([0,i]\)内点的时间之和。
另一方面,最优的\(j\)只有可能在\((i-2m,i-m]\)之间出现,因为若\(j\le i-2m\),可以再次分割\([0,j]\)使得总时间减少。综上所述,
\[f_i\leftarrow \min\left\{i\cdot \mathrm{cnt}_i-\mathrm{sum}_i,\min_{i-2m<j\le i-m}\{f_j+i\cdot (\mathrm{cnt}_i-\mathrm{cnt}_j)-\mathrm{sum}_i+\mathrm{sum}_j\right\}
\]
总复杂度\(O(tm)\)。
考虑继续优化。注意到若\(\mathrm{cnt}_i=\mathrm{cnt}_{i-m}\),即\((i-m,i]\)中不含任何点,那么\(i\)是无用的。此时只需令\(f_i\leftarrow f_{i-m}\)。复杂度优化至\(O(nm^2+t)\)。
Code
#include <cstdio>
#include <iostream>
using namespace std;
const int Maxt=4e6+7;
const int Maxn=507;
int T,n,m;
int f[Maxt],sum[Maxt],cnt[Maxt],t;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
scanf("%d",&t),
T=max(T,t),
sum[t]+=t,
++cnt[t];
for(int i=1;i<T+m;++i)
sum[i]+=sum[i-1],
cnt[i]+=cnt[i-1];
for(int i=1;i<T+m;++i)
{
if(i>=m&&cnt[i]==cnt[i-m])
{
f[i]=f[i-m];
continue;
}
f[i]=i*cnt[i]-sum[i];
for(int j=max(0,i-2*m+1);j<=i-m;++j)
f[i]=min(f[i],f[j]+i*(cnt[i]-cnt[j])-sum[i]+sum[j]);
}
int ans=0x3f3f3f3f;
for(int i=T;i<T+m;++i)
ans=min(ans,f[i]);
printf("%d\n",ans);
}
The man who follow the shadow is just the shadow itself.