仓库建设

https://loj.ac/problem/10189

题目描述

  有\(N\)个工厂建在山上,每个工厂之间有一定的距离,每个工厂有一定的成品和在这个工厂建仓库的代价。成品只能往山脚运,当前\(N\)有一个仓库,求再建若干个仓库后所有成品运至仓库的最小代价。

思路

  由于产品只能从山上往山下运,所以运的必定会是连续的一段,我们记\(f[i]\)为前\(i\)个工厂的最小代价,记\(sum\)\(p\)的前缀和,\(s\)\(p*x[i]\)的前缀和,那么方程为

\[f[i]=min\{f[j]+\sum_{k=j+1}^i p[k]*(x[i]-x[k])\}\\ f[i]=min\{f[j]+x[i]*(sum[i]-sum[j])-s[i]+s[j]\} \]

  化简为一次函数的形式后为

\[f[j]+[j]=x[i]*sum[j]-x[i]*sum[i]+s[i]+f[i] \]

  斜率优化维护一下下凸壳求最小截距即可。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=1e6+10;

ll read()
{
	ll res=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){res=(res<<3)+(res<<1)+(ch^48);ch=getchar();}
	return res*w;
}

ll sum[N],s[N],q[N],f[N],x[N],c[N];
int main()
{
	ll n=read();
	for(ll i=1;i<=n;i++)
	{
		ll p;
		x[i]=read(),p=read(),c[i]=read();
		sum[i]=sum[i-1]+p;
		s[i]=s[i-1]+(p*x[i]);
	}
	ll head=0,tail=0;
	for(ll i=1;i<=n;i++)
	{
		while(head<tail&&(f[q[head+1]]+s[q[head+1]]-f[q[head]]-s[q[head]])
				<=x[i]*(sum[q[head+1]]-sum[q[head]]))head++;
		f[i]=f[q[head]]+x[i]*(sum[i-1]-sum[q[head]])-s[i-1]+s[q[head]]+c[i];
		while(head<tail&&(f[q[tail]]+s[q[tail]]-f[q[tail-1]]-s[q[tail-1]])*(sum[i]-sum[q[tail]])
				>=(f[i]+s[i]-f[q[tail]]-s[q[tail]])*(sum[q[tail]]-sum[q[tail-1]]))tail--;
		q[++tail]=i;
	}
	printf("%lld",f[n]);
}
posted @ 2019-11-13 21:42  fbz  阅读(170)  评论(0编辑  收藏  举报