如前面提到,ABC的汽车工厂有N个工人,他们在一个传送带上生产汽车,工人从左到右排列,编号依次为1到N,采用流水线模式,每个人负责自己的一部分工作。
生产一台汽车需要从1号工人开始,当1号完成他的工作后,2号就会开始工作,然后是3号,最后当N号工人完成他的工作后,整个汽车生产完毕。工人们一共需要生产M台汽车,而且必须按照从1到M的顺序去生产。
对于工人i,他完成自己的工作需要Ti的时间,而对于汽车j,组装复杂度为Fj。那么工人i花在汽车j上的时间为Ti*Fj。当某个工人完成他的工作后,他会同时把汽车交给下一个工人,没有任何时间上的延迟,因此,要保证下一个要接受汽车的工人必须是空闲的。为了满足这个要求,ABC需要为每一台汽车选择一个好时机开始制造。
ABC想知道生产完所有汽车最少需要多少时间。
先来考虑O(n^2)的情况
我们令g[i]表示第i辆汽车开始生产的时间,g[1]=0
那么g[i]=g[i-1]+max(f[i-1]*sum[j]+f[i]*sum[j-1]) 其中sum[j]=Σt[k] (1<=k<=j)
这里表示每一个人都必须在它生产完i-1这辆车之后才能继续第i辆,看到这个式子我们可以考虑斜率优化
对于决策j,k若k优于j那么就有
f[i-1]*s[j]+f[i]*s[j-1]<f[i-1]*s[k]+f[i]*s[k-1]
f[i-1]*(s[j]-s[k])<f[i]*(s[j-1]-s[k-1])
f[i-1]/f[i]<(s[j-1]-s[k-1])/(s[j]-s[k])
因为f[i-1]/f[i]不单调,所以我们维护一个斜率单调递减的上凸壳,让后在上面二分
#pragma GCC opitmize("O3")
#pragma G++ opitmize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 100010
using namespace std;
int n,m,q[N],t=0;
long long f[N],s[N],g[N];
inline double slp(int i,int j){
return (s[i]-s[j])/(double)(s[i-1]-s[j-1]);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
scanf("%lld",s+i),s[i]+=s[i-1];
for(int i=1;i<=m;++i) scanf("%lld",f+i);
for(int i=1;i<=n;++i){
for(;t>1&&slp(i,q[t])>slp(q[t],q[t-1]);--t);
q[++t]=i;
}
g[1]=0;
for(int i=2,l,r,M;i<=m;++i){
l=0; r=t;
for(double k=f[i]/(double)f[i-1];l<r;){
M=l+r>>1;
if(slp(q[M+1],q[M])>k) l=M+1; else r=M;
}
g[i]=g[i-1]+s[q[l]]*f[i-1]-s[q[l]-1]*f[i];
}
printf("%lld
",1ll*g[m]+s[n]*f[m]);
}