题目
有MMM只猫,雇了PPP位饲养员。路边有NNN座山,从111到NNN编号。第iii座山与第i−1i−1i−1座山之间的距离是DiD_iDi 。饲养员都住在111号山上。第iii只猫去HiH_iHi号山玩,玩到时刻TiT_iTi停止,然后在原地等饲养员来接。饲养员们必须回收所有的猫。每个饲养员沿着路从111号山走到NNN号山,把各座山上已经在等待的猫全部接走。饲养员在路上行走需要时间,速度为111米每单位时间。饲养员在每座山上接猫的时间可以忽略,可以携带的猫的数量为无穷大。
你的任务是规划每个饲养员从111号山出发的时间,使得所有猫等待时间的总和尽量小。饲养员出发的时间可以为负。
分析
设Ai=Ti−∑j=1diA_i=T_i-\sum_{j=1}^{d_i}Ai=Ti−∑j=1di
这道题朴素的动态规划,f[i][j]=min{f[i−1][k]+A[i]∗(j−k)−(∑p=k+1ja[p])}f[i][j]=min\{f[i-1][k]+A[i]*(j-k)-(\sum_{p=k+1}^ja[p])\}f[i][j]=min{f[i−1][k]+A[i]∗(j−k)−(∑p=k+1ja[p])}
所以可以发现这是一个斜率优化,发现这个,还可以发现求的是下凸壳
代码
#include <cstdio>
#include <algorithm>
typedef long long ll; ll f[2][100001],a[100001];
int n,m,p,d[100001],q[100001];
ll in(){
ll ans=0; int f=1; char c=getchar();
while ((c<48||c>57)&&c!='-') c=getchar();
if (c=='-') c=getchar(),f=-f;
while (c>47&&c<58) ans=ans*10+c-48,c=getchar();
return ans*f;
}
int main(){
n=in(); m=in(); p=in(); ll ans=1ll<<62;
for (register int i=2;i<=n;i++) d[i]=d[i-1]+in();
for (register int i=1,x;i<=m;i++) x=in(),a[i]=in()-d[x];
std::stable_sort(a+1,a+1+m); bool x=0;
for (register int i=2;i<=m;i++) a[i]+=a[i-1];
std::fill(f[x]+1,f[x]+1+m,1ll<<62);
for (register int i=1;i<=p;i++){
int l=1,r=1; q[1]=0; x^=1;
for (int j=1;j<=m;j++){
while (l<r&&f[x^1][q[l+1]]+a[q[l+1]]-a[q[l]]-f[x^1][q[l]]<(a[j]-a[j-1])*(q[l+1]-q[l])) l++;//队头没有用的需要出队
f[x][j]=f[x^1][q[l]]+(a[j]-a[j-1])*(j-q[l])-(a[j]-a[q[l]]);//动态规划,当然用了滚动数组
while (l<r&&(f[x^1][q[r]]+a[q[r]]-f[x^1][q[r-1]]-a[q[r-1]])*(j-q[r])>(f[x^1][j]+a[j]-a[q[r]]-f[x^1][q[r]])*(q[r]-q[r-1])) r--;//队尾不优的出队
q[++r]=j;//入队
}
ans=std::min(ans,f[x][m]);
}
return !printf("%lld",ans);
}