传送门戳这里>>>
n≤1e6
, 显然还是
O(n)
的做法.
这个题有个条件是只能运往编号更大的工厂的仓库, 这也是写出朴素dp的方程的条件.
我们令
f[i]
表示前
i
个工厂的最小花费, 那么易得
其中这个 t(j,i) 表示将(j,i)这个区间的东西运到 i 的总费用. 很显然, 这个式子要
那么怎么 O(1) 求呢?
考虑类似于前缀和的性质.
我们令 si 为将 (1,i] 这个区间中所有工厂的产品运到 i 的总花费,
t(j,i)=si−sj−cj∗(di−dj)
这样就可以扔进状态转移方程进行斜率优化了… 化完之后的式子是:
f[j]−s[j]+c[j]∗d[j] = d[i] c[j] + f[i]−s[i]−w[i]
然后求的是最小值, 斜率还递增(这好像是最常见的一种了吧?), 那就跟之前一样咯= =
然而还是把演草纸上 d[i]c[j] 的数组名抄反了WA了一次 但为什么可以过样例啊QAQ
然后就是没有压行的代码: (简单的斜率优化似乎总可以写成标准的20行?
#include <cstdio>
const int N=1e6+6;
typedef long long LL;
LL f[N],s[N],c[N];
int q[N],w[N],d[N],n,h,t;
inline int gn(int a=0,char c=0){
for(;c<'0'||c>'9';c=getchar());
for(;c>47&&c<58;c=getchar())a=a*10+c-48;return a;
}
double slope(int x,int y){
return 1.0*(f[x]-s[x]+c[x]*d[x]-f[y]+s[y]-c[y]*d[y])/(c[x]-c[y]);
}
int main(){
n=gn(); for(int i=1;i<=n;++i){
d[i]=gn();c[i]=c[i-1]+gn();w[i]=gn();
s[i]=s[i-1]+c[i-1]*(d[i]-d[i-1]);
}
for(int i=1,j;i<=n;++i){
while(h<t&&slope(q[h],q[h+1])<=d[i]) ++h; j=q[h];
f[i]=f[j]+s[i]-s[j]-c[j]*(d[i]-d[j])+w[i];
while(h<t&&slope(q[t],q[t-1])>=slope(q[t],i)) --t;
q[++t]=i;
}
printf("%lld\n",f[n]);
}