给定长为
n
n
n(1e6)的三个数列X(单调递增),P,C,现在将要其分成若干连续段。
每段
[
l
,
r
]
[l,r]
[l,r]的代价是
C
r
+
Σ
i
=
l
r
P
i
∗
(
X
r
−
X
i
)
C_r+\Sigma_{i=l}^r P_i*(X_r-X_i)
Cr+Σi=lrPi∗(Xr−Xi)。
求代价之和的最小值。
又是分段emm
为了快速求得段代价,我们维护两个前缀和数组:
s
i
=
Σ
j
=
1
i
P
k
s_i=\Sigma_{j=1}^iP_k
si=Σj=1iPk
t
i
=
Σ
j
=
1
i
P
k
X
k
t_i=\Sigma_{j=1}^iP_kX_k
ti=Σj=1iPkXk
设
d
i
d_i
di表示把前
i
i
i个数分好的最小代价,
d
0
=
0
d_0=0
d0=0
d
i
=
m
i
n
{
d
j
+
C
i
+
X
i
(
s
i
−
s
j
)
−
(
t
i
−
t
j
)
}
,
0
<
=
j
<
i
d_i=min\{d_j+C_i+X_i(s_i-s_j) -(t_i-t_j)\},0<=j<i
di=min{dj+Ci+Xi(si−sj)−(ti−tj)},0<=j<i
整理一下: d i = ( C i + X i s i − t i ) + m i n { d j + t j − X i s j } d_i=(C_i+X_is_i-t_i)+min\{d_j+t_j-X_is_j\} di=(Ci+Xisi−ti)+min{dj+tj−Xisj}
设
a
<
b
<
i
a<b<i
a<b<i并且从
b
b
b转移优于从
a
a
a转移,那么:
d
b
+
t
b
−
X
i
s
b
<
d
a
+
t
a
−
X
i
s
a
d_b+t_b-X_is_b<d_a+t_a-X_is_a
db+tb−Xisb<da+ta−Xisa
整理得: ( d b + t b ) − ( d a + t a ) s b − s a < X i \frac{(d_b+t_b)-(d_a+t_a)}{s_b-s_a}<X_i sb−sa(db+tb)−(da+ta)<Xi
由斜率优化原理:
- y i = d i + t i , x i = s i y_i=d_i+t_i,x_i=s_i yi=di+ti,xi=si;
- 维护下凸包;
- 目标斜率是 X i X_i Xi,单调递增;
- 双端队列。
比较基础的一道题目,可以作为模板题使用
WA点:
- 计算dp时i和j搞混
- 计算斜率时把i写成q[r]
看来我真的困了。。。
/* LittleFall : Hello! */
#include <bits/stdc++.h>
using namespace std; using ll = long long; inline int read();
const int M = 1000016, MOD = 1000000007;
ll X[M], P[M], C[M], s[M], t[M], dp[M];
inline ll subx(int a, int b){return s[b]-s[a];}
inline ll suby(int a, int b){return dp[b]+t[b]-dp[a]-t[a];}
inline ll cal(int i, int j){
return (C[i] + X[i]*s[i] - t[i]) + (dp[j] + t[j] - X[i]*s[j]);
}
int q[M], l, r;
int main(void)
{
#ifdef _LITTLEFALL_
freopen("in.txt","r",stdin);
#endif
int n = read();
for(int i=1; i<=n; ++i)
{
X[i] = read(), P[i] = read(), C[i] = read();
s[i] = s[i-1] + P[i];
t[i] = t[i-1] + P[i]*X[i];
}
q[r++] = 0;
for(int i=1; i<=n; ++i)
{
// dp[i] = 1e18;
// for(int j=0; j<i; ++j)
// dp[i] = min(dp[i], cal(i,j));
while(r-l>=2 && suby(q[l], q[l+1])<=subx(q[l], q[l+1])*X[i]) ++l;
dp[i] = cal(i, q[l]);
while(r-l>=2 &&
suby(q[r-2], q[r-1])*subx(q[r-1], i)>=subx(q[r-2], q[r-1])*suby(q[r-1], i)
) --r;
q[r++] = i;
}
cout << dp[n] << "\n";
return 0;
}
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}