[ZJOI2007]仓库建设

洛咕

题意:(N(N<=1000000))个工厂,1到N编号,每个工厂给定距离工厂1的距离(x_i),该工厂物品数量(p_i),该工厂建造仓库的费用(c_i).在几个工厂上建立仓库,没建仓库的工厂要将物品运输到有仓库的工厂,运输费用为物品数量乘两个工厂之间的距离,求最小总花费(建造仓库的费用+运输费用)

分析:设(f[i])表示在i工厂建立仓库的最小总花费.则有(f[i]=f[j]+c[i]+sum_{k=j+1}^{i-1}p[k]*(x[i]-x[k]))

(sumv[i])表示将1到i-1号工厂的所有物品运到i的总运输费用,(sump[i])表示前i个工厂的物品总数,则有

(f[i]=f[j]+c[i]+sumv[i]-sumv[j]-sump[j]*(x[i]-x[j]))

(k<j)且j比k更优,即

(f[j]+c[i]+sumv[i]-sumv[j]-sump[j]*(x[i]-x[j])<f[k]+c[i]+sumv[i]-sumv[k]-sump[k]*(x[i]-x[k]))

整理一下上式得到,

(frac{f[j]-sumv[j]+x[j]*sump[j]-f[k]+sumv[k]-x[k]*sump[k]}{sum[j]-sum[k]}<x[i])

#include<bits/stdc++.h>
#define LL long long
using namespace std;
inline int read(){
    int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
    return s*w;
}
const int N=1000005;
int x[N],p[N],c[N],q[N];
LL sump[N],sumv[N],f[N];
inline double check(int a,int b){return 1.0*(f[a]-sumv[a]+x[a]*sump[a]-f[b]+sumv[b]-x[b]*sump[b])/(sump[a]-sump[b]);}
int main(){
    int n=read();
    for(int i=1;i<=n;i++){
		x[i]=read();p[i]=read();c[i]=read();
		sump[i]=sump[i-1]+p[i];
		sumv[i]=sumv[i-1]+sump[i-1]*(x[i]-x[i-1]);
		f[i]=1e18;
    }
    int l=1,r=1;
    for(int i=1;i<=n;i++){
		while(l<r&&check(q[l+1],q[l])<=x[i])l++;
		f[i]=f[q[l]]+c[i]+sumv[i]-sumv[q[l]]-(x[i]-x[q[l]])*sump[q[l]];
		while(l<r&&check(q[r],q[r-1])>=check(i,q[r]))r--;
		q[++r]=i;
    }
    printf("%lld
",f[n]);
    return 0;
}

原文地址:https://www.cnblogs.com/PPXppx/p/11007457.html