斜率优化 [ZJOI2007] 仓库建设
[ZJOI2007] 仓库建设
题目描述
L 公司有 个工厂,由高到低分布在一座山上,工厂 在山顶,工厂 在山脚。
由于这座山处于高原内陆地区(干燥少雨),L公司一般把产品直接堆放在露天,以节省费用。突然有一天,L 公司的总裁 L 先生接到气象部门的电话,被告知三天之后将有一场暴雨,于是 L 先生决定紧急在某些工厂建立一些仓库以免产品被淋坏。
由于地形的不同,在不同工厂建立仓库的费用可能是不同的。第 个工厂目前已有成品 件,在第 个工厂位置建立仓库的费用是 。
对于没有建立仓库的工厂,其产品应被运往其他的仓库进行储藏,而由于 L 公司产品的对外销售处设置在山脚的工厂 ,故产品只能往山下运(即只能运往编号更大的工厂的仓库),当然运送产品也是需要费用的,一件产品运送一个单位距离的费用是 。
假设建立的仓库容量都都是足够大的,可以容下所有的产品。你将得到以下数据:
- 工厂 距离工厂 的距离 (其中 )。
- 工厂 目前已有成品数量 。
- 在工厂 建立仓库的费用 。
请你帮助 L 公司寻找一个仓库建设的方案,使得总的费用(建造费用 + 运输费用)最小。
输入格式
输入的第一行是一个整数 ,代表工厂的个数。
第 到 行,每行有三个用空格隔开的整数,第 行的整数依次代表 。
输出格式
仅输出一行一个整数,代表最优方案的费用。
样例 #1
样例输入 #1
3
0 5 10
5 3 100
9 6 10
样例输出 #1
32
提示
样例输入输出 解释
在工厂 和工厂 建立仓库,建立费用为 ,运输费用为 ,总费用 。
数据范围与约定
对于 的数据,保证 。
对于 的数据,保证 。
对于 的数据,保证 ,。
对于任意的 ,保证 。
设答案为 ,保证 。
设表示在处建立了一个仓库并且从到所有商品都运到仓库里面的最小代价
麻烦就麻烦在这个sumval怎么快速计算
如果是O(n2)的算法,那这个直接预处理就行,也是O(n2)
但是O(n^2)不能接受,这个状态已经是十分简洁了,没有什么优化的空间,我们要优化,首先就要把这个sumval拆开。
所以最终的方程就是
然后式子里面有 这明显是斜率优化的形式
那就按照斜率优化化简子吧
很明显的式子,自然是单调递增的,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,所以要写成叉积的形式
然后,最后可能是连续的一段,所以要找到这一段里面最小的
要是没注意到这两点,就寄了
服了
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!