题目描述:
S o l u t i o n Solution Solution
一、 O ( n 3 ) d p O(n^3)dp O(n3)dp
设
d
p
[
i
]
dp[i]
dp[i]表示在第
i
i
i个点设立仓库的最小代价。可以通过枚举前一个设置仓库的地方进行转移:
d
p
[
i
]
=
m
i
n
{
d
p
[
j
]
+
∑
k
=
j
+
1
i
(
x
i
−
x
k
)
∗
p
k
}
+
c
i
dp[i]=min\{dp[j]+\sum_{k=j+1}^i(x_i-x_k)*p_k\}+c_i
dp[i]=min{dp[j]+k=j+1∑i(xi−xk)∗pk}+ci
稳定 n 3 n^3 n3
二、 O ( n 2 ) d p O(n^2)dp O(n2)dp
将
d
p
dp
dp式子括号拆掉:
d
p
[
i
]
=
m
i
n
{
d
p
[
j
]
+
∑
k
=
j
+
1
i
x
i
∗
p
k
−
∑
k
=
j
+
1
i
x
k
∗
p
k
}
+
c
i
dp[i]=min\{dp[j]+\sum_{k=j+1}^ix_i*p_k-\sum_{k=j+1}^ix_k*p_k\}+c_i
dp[i]=min{dp[j]+k=j+1∑ixi∗pk−k=j+1∑ixk∗pk}+ci
把
x
i
x_i
xi提到前面:
d
p
[
i
]
=
m
i
n
{
d
p
[
j
]
+
x
i
∗
∑
k
=
j
+
1
i
p
k
−
∑
k
=
j
+
1
i
x
k
∗
p
k
}
+
c
i
dp[i]=min\{dp[j]+x_i*\sum_{k=j+1}^ip_k-\sum_{k=j+1}^ix_k*p_k\}+c_i
dp[i]=min{dp[j]+xi∗k=j+1∑ipk−k=j+1∑ixk∗pk}+ci
将两个
∑
\sum
∑用前缀和优化
设
a
[
i
]
=
∑
j
=
1
i
p
[
j
]
a[i]=\sum_{j=1}^ip[j]
a[i]=∑j=1ip[j],
b
[
i
]
=
∑
j
=
1
i
p
[
j
]
∗
x
[
j
]
b[i]=\sum_{j=1}^ip[j]*x[j]
b[i]=∑j=1ip[j]∗x[j]
然后上面的式子就可以转化成这种形式:
d
p
[
i
]
=
m
i
n
{
d
p
[
j
]
+
x
i
∗
(
a
[
i
]
−
a
[
j
]
)
−
(
b
[
i
]
−
b
[
j
]
)
}
+
c
i
dp[i]=min\{dp[j]+x_i*(a[i]-a[j])-(b[i]-b[j])\}+c_i
dp[i]=min{dp[j]+xi∗(a[i]−a[j])−(b[i]−b[j])}+ci
于是 O ( n 2 ) O(n^2) O(n2)
三、 O ( n ) d p O(n)dp O(n)dp
由于在转移中有关于
i
和
j
i和j
i和j的乘积项,所以这题我们就考虑斜率优化
设
j
>
k
j>k
j>k,
f
[
j
]
<
f
[
k
]
f[j]<f[k]
f[j]<f[k]:
d
p
[
j
]
+
x
i
∗
(
a
[
i
]
−
a
[
j
]
)
−
(
b
[
i
]
−
b
[
j
]
)
<
d
p
[
k
]
+
x
i
∗
(
a
[
i
]
−
a
[
k
]
)
−
(
b
[
i
]
−
b
[
k
]
)
dp[j]+x_i*(a[i]-a[j])-(b[i]-b[j])<dp[k]+x_i*(a[i]-a[k])-(b[i]-b[k])
dp[j]+xi∗(a[i]−a[j])−(b[i]−b[j])<dp[k]+xi∗(a[i]−a[k])−(b[i]−b[k])
拆项移项之后我们得到以下不等式:
(
f
j
+
b
j
)
−
(
f
k
+
b
k
)
a
j
−
a
k
<
x
i
\frac{(f_j+b_j)-(f_k+b_k)}{a_j-a_k}<x_i
aj−ak(fj+bj)−(fk+bk)<xi
由于
x
I
x_I
xI单调递增,所以左边的式子也应该单调递增,用单调队列维护一个下凸壳即可。
C o d e Code Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e6+10;
int n;
int x[N],p[N],c[N];
int a[N],b[N],f[N];
int q[N],l,r;
double Calc(int j,int k){
return ((f[j]+b[j]) - (f[k]+b[k]))*1.0/(a[j]-a[k])*1.0;
}
signed main(){
scanf("%lld",&n);
for (int i = 1; i <= n; i++) scanf("%lld %lld %lld",&x[i],&p[i],&c[i]);
for (int i = 1; i <= n; i++)
a[i] = a[i-1] + p[i] , b[i] = b[i-1] + p[i]*x[i];
for (int i = 1; i <= n; i++){
while (l<r && Calc(q[l+1],q[l]) < x[i]*1.0) l++;//满足条件l+1比l更优,不断弹出
f[i] = f[q[l]] + 1ll*x[i]*(a[i]-a[q[l]])-(b[i]-b[q[l]]) + c[i];//取出队头转移
while (l<r && Calc(q[r],q[r-1]) > Calc(i,q[r])) r--;//将第i个点加入队列,需要保证队列的单调性
q[++r] = i;//加入队列
}
printf("%lld",f[n]);
return 0;
}