description
有(n)个任务,每次可以同时处理一段连续的任务,这些任务将同时处理完,且所需时间等于这些任务单独处理的总时间。每次处理任务前,机器需要(S)的时间准备。每个任务还有一个费用系数,这个任务的费用等于它的完成时刻乘上费用系数。求最小代价。
(nle3 imes10^5)。
sol
怎么(dp)算是一个难点了。
直接计算当前这一次处理的所有任务的总代价,那就需要多记一维状态表示之前已经分了多少批。
考虑在每次耗费时间的时候计算这段时间产生的费用,显然这段时间里尚未处理的任务都会产生相应的费用,而尚未处理的任务恰好是一段后缀。
所以有(dp)式:
[f_i=min{f_j+(t_i-t_j+S) imes(s_n-s_j)}
]
其中(t,s)分别是时间和费用系数的前缀和。
简单推导可知,当满足$$frac{(f_j-t_js_n+t_js_j-Ss_j)-(f_k-t_ks_n+t_ks_k-Ss_k)}{s_j-s_k}le t_i$$且(k<j)时,决策(j)优于决策(k)。
直接斜率优化即可。
然后就发现(WA)了。为什么呢?因为时间有负数!(别问我怎么可能,也别问我怎么知道的)
也就是说(t_i)不一定单调递增。
但是貌似费用系数全是正的,也就是横坐标是满足单调增的,所以维护个下凸壳然后每次决策时二分即可。
复杂度(O(nlog n))。
code
被卡精度了。
为什么弹栈的时候要加那个等于号啊,希望dalao们能教教我。
#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
ll gi(){
ll x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
const int N = 1e6+5;
int n,top;ll S,t[N],s[N],f[N];
struct node{int id;ll x,y;}q[N];
ll getdp(int i,int j){
return f[j]+(t[i]-t[j]+S)*(s[n]-s[j]);
}
int main(){
n=gi();S=gi();
for (int i=1;i<=n;++i) t[i]=t[i-1]+gi(),s[i]=s[i-1]+gi();
for (int i=1;i<=n;++i){
int l=1,r=top,res=0;
while (l<=r){
int mid=l+r>>1;
if ((q[mid].y-q[mid-1].y)<1ll*t[i]*(q[mid].x-q[mid-1].x)) res=mid,l=mid+1;
else r=mid-1;
}
f[i]=getdp(i,q[res].id);
node tmp=(node){i,s[i],f[i]-s[n]*t[i]+s[i]*t[i]-s[i]*S};
while (top&&(q[top].y-q[top-1].y)*(tmp.x-q[top].x)>=(tmp.y-q[top].y)*(q[top].x-q[top-1].x)) --top;
q[++top]=tmp;
}
printf("%lld
",f[n]);
return 0;
}