题目链接:洛谷 P2120 / LOJ #10189
题意
自山顶向下有 n n n 个工厂,分别距离山顶 X [ i ] ( X 1 = 0 ) X[i](X_1=0) X[i](X1=0),内有 P [ i ] P[i] P[i] 份货物。现在要修建多个仓库,并将所有货物运送到不高于其所在工厂的仓库中;在各个工厂修仓库的费用分别为 C [ i ] C[i] C[i],将每 1 份货物每运送 1 单位距离花费 1。问最小总费用。 n ≤ 1 0 6 n\leq 10^6 n≤106。
题解
记 s [ i ] = ∑ j = 1 i P [ j ] X [ j ] , s p [ i ] = ∑ j = 1 i P [ j ] s[i]=\sum\limits_{j=1}^i P[j]X[j],sp[i]=\sum\limits_{j=1}^iP[j] s[i]=j=1∑iP[j]X[j],sp[i]=j=1∑iP[j]。设 f [ i ] f[i] f[i] 为解决编号在 i i i 以内所有工厂的货物存储的最小费用,则:
f [ i ] = min { f [ j ] + c [ i ] + ( s p [ i ] − s p [ j ] ) x [ i ] − ( s [ i ] − s [ j ] ) } f[i]=\min\{f[j]+c[i]+(sp[i]-sp[j])x[i]-(s[i]-s[j])\} f[i]=min{f[j]+c[i]+(sp[i]−sp[j])x[i]−(s[i]−s[j])}
f [ j ] + s [ j ] = x [ i ] s p [ j ] + ( f [ i ] + s [ i ] − c [ i ] − x [ i ] ∗ s p [ i ] ) y = k x + b \begin{aligned}&f[j]+s[j]&=&x[i] &sp[j]\ +\ &(f[i]+s[i]-c[i]-x[i]*sp[i])\\ &y&=&k &x\ +\ &b\end{aligned} f[j]+s[j]y==x[i]ksp[j] + x + (f[i]+s[i]−c[i]−x[i]∗sp[i])b
标准的一个斜率优化的式子,维护一个下凸壳。
代码:
/**********
Author: WLBKR5
Problem: loj 10189, luogu 2120
Name: 仓库建设
Source: ZJOI2007
Algorithm: 斜率优化
Date: 2020/06/01
Statue: accepted
Submission: loj.ac/submission/821917, www.luogu.com.cn/record/34071226
**********/
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int getint(){
int ans=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
ans=ans*10+c-'0';
c=getchar();
}
return ans*f;
}
const int N=1e6+10;
ll x[N],c[N];
ll s[N],sp[N],f[N];
long double _(int a,int b){
return (f[a]+s[a]-f[b]-s[b])*1.0/(sp[a]-sp[b]);
}
int main(){
//let s=\sum p*x
//f[i]=f[j]+c[i]+(sp[i]-sp[j])*x[i]-s[i]+s[j]
//f[j]+s[j] = x[i] * sp[j] + f[i]+s[i]-c[i]-x[i]*sp[i]
// y = k * x + b
int n=getint();
for(int i=1;i<=n;i++){
x[i]=getint();
int p=getint();
c[i]=getint();
s[i]=s[i-1]+p*x[i];
sp[i]=sp[i-1]+p;
}
deque<int>d;d.push_back(0);
for(int i=1;i<=n;i++){
while(d.size()>1&&_(d[0],d[1])<x[i])d.pop_front();
int j=d.size()?d.front():0;
f[i]=f[j]+c[i]+(sp[i]-sp[j])*x[i]-s[i]+s[j];
while(d.size()>1&&_(d[d.size()-2],d[d.size()-1])>_(d[d.size()-1],i))
d.pop_back();
d.push_back(i);
}
cout<<f[n];
return 0;
}