洛谷 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;
}