洛谷 P5017 [NOIP2018 普及组] 摆渡车(斜率优化dp)

传送门


解题思路

设dp[i]表示在第i秒发车时前面的人一共等候了多少分钟。
朴素方程就是枚举 \(j=0\to (i-m)\)

\[dp[i]=min(dp[i],dp[j]+i\times(cnt[i]-cnt[j])-(a[i]-a[j])) \]

其中 \(cnt[i]\) 为前 i 秒的人数,\(a[i]\) 为前 i 秒的所有人的到达时间的和。
把式子拆开并移项,就变成了 y=kx+b 的形式:

\[dp[j]+a[j]=i\times cnt[j]+dp[i]+a[i]-i\times cnt[i] \]

其中 y=dp[j]+a[j],x=cnt[j],斜率就是 i。
然后就套上斜率优化板子即可。
注意最后答案是

\[ans=\min_{i=t}^{t+m}{dp[i]} \]

AC代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=4e6+105;
int n,m,t[maxn],l=1,r=1,q[maxn],dp[maxn],cnt[maxn],a[maxn],ans=1e9;
inline double cal(int l,int r){
	return 1.0*(dp[r]+a[r]-dp[l]-a[l])/(cnt[r]==cnt[l]?1e-9:cnt[r]-cnt[l]);
}
int main(){
	ios::sync_with_stdio(false);
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>t[i],t[i]++;
	sort(t+1,t+n+1);
	int now=1;
	for(int i=1;i<=t[n]+m;i++){
		a[i]=a[i-1];
		cnt[i]=cnt[i-1];
		while(now<=n&&t[now]==i) now++,a[i]+=i,cnt[i]++;
	}
	q[1]=dp[0]=0;
	for(int i=1;i<=t[n]+m;i++){
		while(l<r&&cal(q[l],q[l+1])<=i) l++;
		dp[i]=dp[q[l]]+i*(cnt[i]-cnt[q[l]])-(a[i]-a[q[l]]);
		if(i<m) continue;
		while(l<r&&cal(q[r-1],q[r])>=cal(q[r],i-m+1)) r--;
		q[++r]=i-m+1;
	}
	for(int i=t[n];i<=t[n]+m;i++) ans=min(ans,dp[i]);
	cout<<ans;
    return 0;
}
posted @ 2021-09-11 20:28  尹昱钦  阅读(156)  评论(0编辑  收藏  举报