【BZOJ2726】【SDOI2012】—任务安排(斜率优化dp)
有一个显然的
表示前天的最小花费
是费用的前缀和
是时间的前缀和
令
考虑2个决策点如果比更优
则
那就变成斜率优化的套路了
注意斜率不要直接除,精度要爆炸的
交叉相乘判大小
对很友好?比快了接近一倍
#include<bits/stdc++.h>
using namespace std;
const int RLEN=1<<20|1;
#define ll long long
#define re register
inline char gc(){
static char ibuf[RLEN],*ob,*ib;
(ib==ob)&&(ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
return (ob==ib)?EOF:*ib++;
}
inline int read(){
char ch=gc();
int res=0,f=1;
while(!isdigit(ch))f^=(ch=='-'),ch=gc();
while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
return f?res:-res;
}
const int N=300005;
const double eps=1e-6;
struct point{
double x,y;
point(double _x=0,double _y=0):x(_x),y(_y){}
friend inline point operator -(const point &a,const point &b){
return point(a.x-b.x,a.y-b.y);
}
inline double slope(){
return x/y;
}
}p[N];
int n,stk[N],top;
ll f[N],s,tot[N],t[N];
inline void dp(int i,int j){
f[i]=f[j]+(t[i]-t[j]+s)*(tot[n]-tot[j]);
}
signed main(){
n=read(),s=read();
for(re int i=1;i<=n;++i)t[i]=t[i-1]+read(),tot[i]=tot[i-1]+read();
for(re int i=1;i<=n;++i){
re int l=1,r=top,res=0;
while(l<=r){
int mid=(l+r)>>1;
point tp=(p[stk[mid]]-p[stk[mid-1]]);
if(tp.y<=t[i]*tp.x)l=mid+1,res=mid;
else r=mid-1;
}
dp(i,stk[res]);
p[i].x=tot[i],p[i].y=f[i]-tot[n]*t[i]+tot[i]*t[i]-s*tot[i];
while(top&&(p[stk[top]]-p[stk[top-1]]).y*(p[i]-p[stk[top]]).x>=(p[i]-p[stk[top]]).y*(p[stk[top]]-p[stk[top-1]]).x)top--;
stk[++top]=i;
}
cout<<f[n];
}