P2365 任务安排 / [FJOI2019]batch(斜率优化dp)

P2365 任务安排

batch:$n<=10000$

斜率优化入门题

$n^{3}$的dp轻松写出

但是枚举这个分成多少段很不方便

我们利用费用提前的思想,提前把这个烦人的$S$在后面的贡献先算掉

设$sv[i],st[i]$为费用、时间的前缀和

于是我们就可以得出一个$n^{2}$的方程

$f[i]=f[j]+(sv[i]-sv[j])*st[i]+(sv[n]-sv[j])*S$

拆开:$f[i]=f[j]+sv[i]*st[i]-sv[j]*st[i]+sv[n]*S-sv[j]*S$

移项:$f[j]=(S+st[i])*sv[j]+f[i]-sv[i]*st[i]-sv[n]*S$

再用$y=k*x+b$的套路带进去

$f[j]=(S+st[i])*sv[j]+f[i]-sv[i]*st[i]-sv[n]*S$

$y=k*x+b$

$y=f[j]$

$x=sv[j]$

$k=S+st[i]$(显然随着$i$增大而单调递增)

$b=f[i]-sv[i]*st[i]-sv[n]*S$

于是问题又转化成:找到一个使$b$最小的$(x,y)$

这样就能使$f[i]$最小

考虑到$k$单调递增,$x$也单调递增

我们就可以快乐地用单调队列维护下凸包辣

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef double db;
#define N 10005
db sv[N],st[N],f[N];
int n,S,L,R,h[N];
inline db X(int x){return sv[x];}
inline db Y(int x){return f[x];}
inline db K(int x,int y){return (Y(x)-Y(y))/(X(x)-X(y));}
int main(){
    scanf("%d%d",&n,&S);
    for(int i=1;i<=n;++i){
        scanf("%lf%lf",&st[i],&sv[i]);
        st[i]+=st[i-1]; sv[i]+=sv[i-1];
    }L=R=1;
    for(int i=1;i<=n;++i){
        while(L<R&&K(h[L],h[L+1])<=S+st[i]) ++L;
        f[i]=f[h[L]]+(sv[i]-sv[h[L]])*st[i]+(sv[n]-sv[h[L]])*S;
        while(L<R&&K(h[R-1],h[R])>K(h[R],i)) --R;
        h[++R]=i;
    }printf("%.0lf",f[n]);
    return 0;
}
原文地址:https://www.cnblogs.com/kafuuchino/p/10743781.html