题意
L 公司有 n n n 个工厂,由高到低分布在一座山上,工厂 1 1 1 在山顶,工厂 n n n 在山脚。
由于地形的不同,在不同工厂建立仓库的费用可能是不同的。第 i i i 个工厂目前已有成品 p i p_i pi 件,在第 i i i 个工厂位置建立仓库的费用是 c i c_i ci。
对于没有建立仓库的工厂,其产品应被运往其他的仓库进行储藏,而由于 L 公司产品的对外销售处设置在山脚的工厂 n n n,故产品只能往山下运(即只能运往编号更大的工厂的仓库),当然运送产品也是需要费用的,一件产品运送一个单位距离的费用是 1 1 1。
假设建立的仓库容量都都是足够大的,可以容下所有的产品。你将得到以下数据:
- 工厂 i i i 距离工厂 1 1 1 的距离 x i x_i xi(其中 x 1 = 0 x_1=0 x1=0)。
- 工厂 i i i 目前已有成品数量 p i p_i pi。
- 在工厂 i i i 建立仓库的费用 c i c_i ci。
请你帮助 L 公司寻找一个仓库建设的方案,使得总的费用(建造费用 + 运输费用)最小。
1 ≤ n ≤ 1 0 6 1 \leq n \leq 10^6 1≤n≤106, 0 ≤ x i , p i , c i < 2 31 0 \leq x_i,p_i,c_i < 2^{31} 0≤xi,pi,ci<231。 ∀ 1 ≤ i < n , x i < x i + 1 \forall1 \leq i < n,x_i<x_{i+1} ∀1≤i<n,xi<xi+1。
设答案为 a n s ans ans,保证 a n s + ∑ i = 1 n p i x i < 2 63 ans + \sum\limits_{i = 1}^{n} p_ix_i < 2^{63} ans+i=1∑npixi<263。
思路
考虑朴素的 dp,设
f
i
f_i
fi 表示工厂
i
i
i 建仓库,前面
i
−
1
i-1
i−1 个工厂处理完成的最小费用。那么有:
f
i
=
min
j
=
1
i
−
1
{
f
j
+
c
i
+
∑
k
=
j
+
1
i
p
k
(
x
i
−
x
k
)
}
f_i=\min_{j=1}^{i-1}\left \{ f_j+c_i+\sum_{k=j+1}^{i}p_k(x_i-x_k)\right \}
fi=j=1mini−1⎩
⎨
⎧fj+ci+k=j+1∑ipk(xi−xk)⎭
⎬
⎫
考虑把求和那一坨变得好看些,设:
s
1
j
=
∑
k
=
1
j
p
k
x
k
,
s
2
j
=
∑
k
=
1
j
p
k
s1_j=\sum_{k=1}^{j}p_kx_k,s2_j=\sum_{k=1}^{j}p_k
s1j=k=1∑jpkxk,s2j=k=1∑jpk
那么变形一下原来的式子:
f
j
+
c
i
+
x
i
∑
k
=
j
+
1
i
p
k
−
∑
k
=
j
+
1
i
p
k
x
k
f_j+c_i+x_i\sum_{k=j+1}^{i}p_k-\sum_{k=j+1}^{i}p_kx_k
fj+ci+xik=j+1∑ipk−k=j+1∑ipkxk
f j + c i + x i ( s 2 i − s 2 j ) − ( s 1 i − s 1 j ) f_j+c_i+x_i(s2_i-s2_j)-(s1_i-s1_j) fj+ci+xi(s2i−s2j)−(s1i−s1j)
设两个决策点
j
1
<
j
2
j1<j2
j1<j2 且
j
2
j2
j2 优于
j
1
j1
j1,那么:
f
j
1
+
c
i
+
x
i
(
s
2
i
−
s
2
j
1
)
−
(
s
1
i
−
s
1
j
1
)
>
f
j
2
+
c
i
+
x
i
(
s
2
i
−
s
2
j
2
)
−
(
s
1
i
−
s
1
j
2
)
f_{j1}+c_i+x_i(s2_i-s2_{j1})-(s1_i-s1_j1)>f_{j2}+c_i+x_i(s2_i-s2_{j2})-(s1_i-s1_{j2})
fj1+ci+xi(s2i−s2j1)−(s1i−s1j1)>fj2+ci+xi(s2i−s2j2)−(s1i−s1j2)
x i ( s 2 j 2 − s 2 j 1 ) > f j 2 − f j 1 + s 1 j 2 − s 1 j 1 x_i(s2_{j2}-s2_{j1})>f_{j2}-f_{j1}+s1_{j2}-s1_{j1} xi(s2j2−s2j1)>fj2−fj1+s1j2−s1j1
因为
s
2
j
2
>
s
2
j
1
s2_{j2}>s2_{j1}
s2j2>s2j1,所以直接除过去:
x
i
>
f
j
2
−
f
j
1
+
s
1
j
2
−
s
1
j
1
s
2
j
2
−
s
2
j
1
x_i>\frac{f_{j2}-f_{j1}+s1_{j2}-s1_{j1}}{s2_{j2}-s2_{j1}}
xi>s2j2−s2j1fj2−fj1+s1j2−s1j1
f j 2 − f j 1 + s 1 j 2 − s 1 j 1 s 2 j 2 − s 2 j 1 < x i \frac{f_{j2}-f_{j1}+s1_{j2}-s1_{j1}}{s2_{j2}-s2_{j1}}<x_i s2j2−s2j1fj2−fj1+s1j2−s1j1<xi
s
l
o
p
e
<
x
i
slope<x_i
slope<xi 且
x
i
x_i
xi 单调递增,那么和玩具装箱是同一类,维护斜率单调递增,新建决策点扔到队首。再看看那一张结论图:
因为最终“集中地”不一定在
n
n
n,即可能
p
n
=
0
p_n=0
pn=0,设
p
i
≠
0
,
i
=
i
m
a
x
∈
[
1
,
n
]
p_i\ne0,i=imax\in[1,n]
pi=0,i=imax∈[1,n],那么应当计算
[
i
,
n
]
[i,n]
[i,n] 的答案,找费用最少得“集中地”。
a
n
s
=
min
j
=
i
n
f
j
ans=\min_{j=i}^{n}f_j
ans=j=iminnfj
还要注意一点就是有精度问题,因为这个在洛谷被卡了很久,因此把斜率式子的分子分母拆开来交叉相乘进行对比。具体细节建代码。
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define dd double
#define ls u<<1
#define rs u<<1|1
const ll N=1e6+9,inf=0x7f7f7f7f;
ll n,x[N],p[N],c[N];
ll s1[N],s2[N];
ll q[N],f[N];
ll slope_up(ll j1,ll j2)
{
return f[j2]-f[j1]+s1[j2]-s1[j1];
}
ll slope_down(ll j1,ll j2)
{
return s2[j2]-s2[j1];
}
/*dd slope(ll j1,ll j2)
{
if(s2[j2]==s2[j1])return inf;
return (dd)(f[j2]-f[j1]+s1[j2]-s1[j1])/(dd)(s2[j2]-s2[j1]);
}惨死现场,被#13卡*/
int main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld%lld%lld",&x[i],&p[i],&c[i]);
s1[i]=s1[i-1]+x[i]*p[i];
s2[i]=s2[i-1]+p[i];
}
ll hh=0,tt=0;
for(int i=1;i<=n;i++)
{
while(hh<tt&&slope_up(q[hh],q[hh+1])<x[i]*slope_down(q[hh],q[hh+1]))hh++;
f[i]=f[q[hh]]+c[i]-(s1[i]-s1[q[hh]])+x[i]*(s2[i]-s2[q[hh]]);
while(hh<tt&&slope_up(q[tt],i)*slope_down(q[tt-1],q[tt])<slope_up(q[tt-1],q[tt])*slope_down(q[tt],i))tt--;
q[++tt]=i;
}
ll i=n,ans=f[n];
while(i&&p[i]==0)
{
i--;
ans=min(ans,f[i]);
}
printf("%lld",ans);
return 0;
}