Batch Scheduling
$ solution: $
这应该是斜率优化中最经典的一道题目,虽然之前已经写过一道 $ catstransport $ 的题解了,但还是来回顾一下吧,这道题其实较那一道还是难一些,只不过 $ catstransport $ 很难找到最好码代码的式子。
首先单调队列优化是用来优化一些转移方程里面存在只与枚举的决策或当前状态中的单独某一个存在关系的项的动态规划,而我们的斜率优化是用来优化一些转移方程里面存在与枚举的决策和当前状态都有直接关系的项的动态规划的(当然斜率优化还需要这个项是随着枚举呈单调性的)。
我们先来分析一下这道题目,要将某些任务按顺序分批次处理,同一批次共用一个时间权值,每批次前要花时间预处理。因为它需要按时间顺序处理,所以我们可以想到线性DP,而且子任务的最优性是可以推广到全局的。可能有人会说最后一个限制会有后效性,但其实它很容易消去,我们只要在每一个批次转移时减去后面所有的任务会因为这个预处理的拖延而产生的额外花费即可。这样我们可以列出转移方程:
因为题目没说一定要将这些任务划分成多少批次,所以我们可以直接设 $ F[i] $ 表示将前 $ i $ 分任务划分完毕之后的最少花费,然后我们预处理处时间的前缀和 $ T[i] $ 还有费用的前缀和 $ S[i] $ 然后转移可以这样:
$ F[i]=^{min}_{0leq k<j}{F[j]+S imes (S[N]-S[j])+T[i] imes (S[i]-S[j]) } $
这个式子是 $ n^2 $ 的复杂度,数据范围再次对我们说不,我们还要在优化:将与 $ j $ 无关的数提取出来,并去掉括号:
$ F[i]=^{min}_{0leq k<j}{F[j]+S imes S[N]-S imes S[j]+T[i] imes S[i]-T[i] imes S[j] } $
$ F[i]=^{min}_{0leq k<j}{F[j]-S imes S[j]-T[i] imes S[j] }+S imes S[N]+T[i] imes S[i] $
然后我们发现 $ max $ 函数里有与枚举的决策 $ j $ 和当前状态 $ i $ 都有直接关系的项 ( $ T[i] imes S[j] $ )而且 $ T[i] $ 是单调递增的,于是我们就可以用斜率优化了。将 $ S[j] $ 最为 $ x $ 轴,将 $ F[i]+S imes S[j] $ 作为 $ y $ 轴,然后每一个决策集合(我们所有的可以用的 $ j $ 都变成一个点),我们知道 $ T[i] $ (斜率)是单调递增的,所以只要这些点的的下凸壳即可!
$ code: $
#include<iostream>
#include<cstdio>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#define ll long long
#define db double
#define inf 0x7fffffff
#define rg register int
using namespace std;
int n,s,sr=1;
ll v[300005];
ll t[300005];
ll f[300005];
ll q[300005];
inline int qr(){
register char ch; register bool sign=0; rg res=0;
while(!isdigit(ch=getchar())) if(ch=='-')sign=1;
while(isdigit(ch)) res=res*10+(ch^48),ch=getchar();
return sign?-res:res;
}
inline int get(int k){
if(sr<2)return q[sr];
rg l=1,r=sr-1,mid;
while(l<=r){
mid=(l+r)>>1;
if(f[q[mid+1]]-f[q[mid]]<k*(v[q[mid+1]]-v[q[mid]]))l=mid+1;
else r=mid-1;
}return q[l];
}
int main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
n=qr(); s=qr();
for(rg i=1;i<=n;++i){
t[i]=t[i-1]+qr();
v[i]=v[i-1]+qr();
} q[1]=0;
for(rg i=1;i<=n;++i){
rg j=get(s+t[i]);
f[i]=f[j]-(s+t[i])*v[j]+v[i]*t[i]+s*v[n];
while(sr>1&&(f[i]-f[q[sr]])*(v[q[sr]]-v[q[sr-1]])<=(f[q[sr]]-f[q[sr-1]])*(v[i]-v[q[sr]])) --sr;
q[++sr]=i;
}printf("%lld
",f[n]);
return 0;
}