题解 [ZJOI2007]仓库建设(LOJ #10189 / 洛谷 P2120)【斜率优化DP】

题目链接:洛谷 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 n106

题解

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=1iP[j]X[j],sp[i]=j=1iP[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; 
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值