[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\n",f[n]);
    return 0;
}

posted on 2019-06-12 08:26  PPXppx  阅读(110)  评论(0编辑  收藏  举报