斜率优化 [ZJOI2007] 仓库建设

[ZJOI2007] 仓库建设

题目描述

L 公司有 n 个工厂,由高到低分布在一座山上,工厂 1 在山顶,工厂 n 在山脚。

由于这座山处于高原内陆地区(干燥少雨),L公司一般把产品直接堆放在露天,以节省费用。突然有一天,L 公司的总裁 L 先生接到气象部门的电话,被告知三天之后将有一场暴雨,于是 L 先生决定紧急在某些工厂建立一些仓库以免产品被淋坏。

由于地形的不同,在不同工厂建立仓库的费用可能是不同的。第 i 个工厂目前已有成品 pi 件,在第 i 个工厂位置建立仓库的费用是 ci

对于没有建立仓库的工厂,其产品应被运往其他的仓库进行储藏,而由于 L 公司产品的对外销售处设置在山脚的工厂 n,故产品只能往山下运(即只能运往编号更大的工厂的仓库),当然运送产品也是需要费用的,一件产品运送一个单位距离的费用是 1

假设建立的仓库容量都都是足够大的,可以容下所有的产品。你将得到以下数据:

  • 工厂 i 距离工厂 1 的距离 xi(其中 x1=0)。
  • 工厂 i 目前已有成品数量 pi
  • 在工厂 i 建立仓库的费用 ci

请你帮助 L 公司寻找一个仓库建设的方案,使得总的费用(建造费用 + 运输费用)最小。

输入格式

输入的第一行是一个整数 n,代表工厂的个数。

2(n+1) 行,每行有三个用空格隔开的整数,第 (i+1) 行的整数依次代表 xi, pi, ci

输出格式

仅输出一行一个整数,代表最优方案的费用。

样例 #1

样例输入 #1

3
0 5 10
5 3 100
9 6 10

样例输出 #1

32

提示

样例输入输出 1 解释

在工厂 1 和工厂 3 建立仓库,建立费用为 10+10=20 ,运输费用为 (95)×3=12,总费用 32

数据范围与约定

对于 20% 的数据,保证 n500

对于 40% 的数据,保证 n104

对于 100% 的数据,保证 1n1060xi,pi,ci<231

对于任意的 1i<n,保证 xi<xi+1

设答案为 ans,保证 ans+i=1npixi<263
f[i]表示在i处建立了一个仓库并且从1i所有商品都运到仓库里面的最小代价

f[i]=min1ki(f[k]+sumval(i,k)+c[i])

麻烦就麻烦在这个sumval怎么快速计算
如果是O(n2)的算法,那这个直接预处理就行,也是O(n2)
但是O(n^2)不能接受,这个状态已经是十分简洁了,没有什么优化的空间,我们要优化,首先就要把这个sumval拆开。

sumval[i][j]sumval[i][j+1],x[i]ii+1,cnt[i]1isumval[st][st]=0sumval[st][ed]=j=st+1jedp[j](x[i]x[j])sumval[st][ed]=j=st+1jedp[j]x[i]p[j]x[j]prepx[i]p[i]x[i],prep[i]p[i]sumval[st][ed]=x[i]prep[i]x[i]prep[k]prepx[i]+prepx[k]

所以最终的方程就是

f[i]=min1ki(f[k]+c[i]+x[i]prep[i]x[i]prep[k])prepx[i]+prepx[k])f[i]=min1ki(f[k]x[i]prep[k]+prepx[k])+c[i]+x[i]prep[i]prepx[i]

然后式子里面有cnt[i]×prex[k] 这明显是斜率优化的形式
那就按照斜率优化化简子吧

f[i]=f[k]x[i]prep[k]+prepx[k]+c[i]+x[i]prep[i]prepx[i]f[k]+prepx[k]=x[i]prep[k]c[i]x[i]prep[i]+prepx[i]+f[i]

很明显的式子,x[i]自然是单调递增的,k的范围是1到i,单调移动,非常好性质,使我的dp旋转

算是好写的吧。。没有二分,没有平衡树,ZJOI他真的,我哭死

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll read() {
	char c=getchar();ll a=0,b=1;
	for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1;
	for(;c>='0'&&c<='9';c=getchar())a=a*10+c-48;return a*b;
}
ll n;
ll x[2000001],p[2000001],c[2000001];
ll prep[2000001],prepx[2000001],f[2000001];
ll l,r;
ll q[2000001];
inline ll Y(ll k)
{
	return f[k]+prepx[k];
}
inline ll X(ll k)
{
	return prep[k];
}
inline ll chaji(ll k1,ll k2,ll k3)
{
	ll Y1=Y(k3)-Y(k2),Y2=Y(k3)-Y(k1);
	ll X1=prep[k3]-prep[k2],X2=prep[k3]-prep[k1];
	if(X1==X2)return -1;
//	if(X1==X2)return 
	return Y1*X2-Y2*X1;
}
int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	n=read();
	for(ll i=1;i<=n;i++)
	{
		x[i]=read();
		p[i]=read();
		prep[i]=prep[i-1]+p[i];
		prepx[i]=prepx[i-1]+p[i]*x[i];
		c[i]=read();
//		if(p[i]==0)i--,n--;
	}
//	f[1]=c[1];
	l=1,r=1;
	for(ll i=1;i<=n;i++)
	{
		while(l<r&&(Y(q[l+1])-Y(q[l]))<=x[i]*(X(q[l+1])-X(q[l])))l++;
		f[i]=f[q[l]]-x[i]*prep[q[l]]+prepx[q[l]]+c[i]+x[i]*prep[i]-prepx[i];
		while(l<r&&(chaji(q[r-1],q[r],i)<0))r--;
		q[++r]=i;
	}
	int cnt=n;
	for(;cnt>=1;cnt--)
	{
		if(p[cnt]!=0)break;
	}
	ll ans=2147483647;
	for(int i=cnt;i<=n;i++)ans=min(ans,f[i]);
	cout<<ans<<endl;
	return 0;
}

哭个p,tnnd,全是坑
首先,这个斜率可能是0,所以要写成叉积的形式
然后,最后可能是连续的一段p[i]=0,所以要找到这一段里面最小的f[i]

要是没注意到这两点,就寄了

服了

posted @   HL_ZZP  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示