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);
}
posted @ 2020-10-23 10:52  Anverking  阅读(102)  评论(0编辑  收藏  举报