P2365 任务安排 / [FJOI2019]batch(斜率优化dp)
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; }