题目链接:Click here
Solution:
我们设(f[i])表示我们把(i)单独划分出来后的对总答案的最小代价,(c[i])表示花费的前缀和,(t[i])表示时间的前缀和
[f[i]=f[j]+t[i] imes (c[i]-c[j])+S imes (c[n]-c[j])\
f[i]=f[j]+t[i] imes c[i]-t[i] imes c[j]+S imes c[n]-S imes c[j]\
-f[j]=t[i] imes c[i]-t[i] imes c[j]+S imes c[n]-S imes c[j]-f[i]\
-f[j]=(-t[i]-S) imes c[j]+t[i] imes c[i]+S imes c[n]-f[i]\
f[j]=(t[i]+S) imes c[j]-t[i] imes c[i]-S imes c[n]+f[i]\
]
我们把这个式子看作形如:(y=kx+b)的一次函数,(c[j])看作自变量,则式子的斜率是确定的,截距只有(f[i])不确定
则我们想让截距(b)尽量的小,则我们需要用将这个斜率确定的直线向上平移碰到的第一个点(二元组((c[j],f[j])))来更新
因为这样我们得到的截距才会是最小的,那么我们只要维护一个下凸包就好了
为什么是下凸包呢?考虑选取下凸包中连续三个点(a_1,a_2,a_3),由于他是一个下凸包,我们可以得出(k(a_2,a_1)<k(a_3,a_2))
其中(k(a,b))表示(a,b)两点连线的斜率。当一个点能更新当前状态时,它一定是下凸包上的点,一个向上凸的点是不可能转移的(可以画图理解)
同时我们注意到它的斜率是单调递增的,所以我们可以维护队首为答案
Code:
#include<bits/stdc++.h>
using namespace std;
const int N=5e3+1;
int n,s,t[N],c[N],f[N];
int head,tail,q[N];
int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
int main(){
n=read();s=read();
for(int i=1;i<=n;i++){
t[i]=read(),c[i]=read();
t[i]+=t[i-1],c[i]+=c[i-1];
}
for(int i=1;i<=n;i++){
while(head<tail&&(f[q[head+1]]-f[q[head]]<=(t[i]+s)*(c[q[head+1]]-c[q[head]]))) ++head;
f[i]=f[q[head]]+t[i]*(c[i]-c[q[head]])+s*(c[n]-c[q[head]]);
while(head<tail&&(f[i]-f[q[tail]])*(c[q[tail]]-c[q[tail-1]])<=(f[q[tail]]-f[q[tail-1]])*(c[i]-c[q[tail]])) --tail;
q[++tail]=i;
}printf("%d
",f[n]);
return 0;
}