令 \(f_i\) 表示在 \(i\) 工厂建立仓库,\(i+1\sim n-1\) 工厂不建立仓库的最小总费用。对 \(p\) 做一遍前缀和。
转移直接枚举上一个建立的工厂在哪儿:
\[f_i=f_j-(p_i-p_j)\times(x_n-x_i)(j<i)+c_i \]考虑两个决策 \(j,k(j<k)\),若 \(j\) 比 \(k\) 优:
\[f_j-(p_i-p_j)\times(x_n-x_i)<f_k-(p_i-p_k)\times(x_n-x_i) \] \[f_j-f_k<(x_n-x_i)\times(p_i-p_j-p_i+p_k) \] \[\dfrac{f_k-f_j}{p_k-p_j}>x_i-x_n \]因为 \(x_i-x_n\) 随着 \(i\) 的增大单调递增,所以维护一个下凸壳。
时间复杂度 \(O\left(n\right)\)。
code:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define N 1000005
#define Db double
#define Min(x,y)((x)<(y)?x:y)
#define For(i,x,y)for(i=x;i<=(y);i++)
ll p[N],f[N];
int x[N],c[N],que[N];
int read()
{
int A;
bool K;
char C;
C=A=K=0;
while(C<'0'||C>'9')K|=C=='-',C=getchar();
while(C>'/'&&C<':')A=(A<<3)+(A<<1)+(C^48),C=getchar();
return(K?-A:A);
}
inline Db slope(int j,int k)
{
return Db(f[k]-f[j])/Db(p[k]-p[j]);
}
int main()
{
int n,i,head,tail;
n=read();
For(i,1,n)
{
x[i]=read(),p[i]=read(),c[i]=read();
f[0]-=p[i]*x[i];
p[i]+=p[i-1];
}
f[0]+=p[n]*x[n];
head=tail=1;
For(i,1,n-1)
{
while(head<tail&&slope(que[head],que[head+1])<=x[i]-x[n])head++;
f[i]=f[que[head]]-(p[i]-p[que[head]])*(x[n]-x[i])+c[i];
/*cout<<i<<' '<<f[i]<<' '<<que[head]<<endl;*/
while(head<tail&&slope(que[tail-1],que[tail])>=slope(que[tail],i))tail--;
que[++tail]=i;
}
For(i,1,n-1)f[0]=Min(f[0],f[i]);
cout<<f[0]+c[n];
return 0;
}