【题解】洛谷P2120 [ZJOI2007]仓库建设 斜率优化

给定长为 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(XrXi)
求代价之和的最小值。


又是分段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(sisj)(titj)}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+Xisiti)+min{dj+tjXisj}

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+tbXisb<da+taXisa

整理得: ( 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 sbsa(db+tb)(da+ta)<Xi

由斜率优化原理:

  1. y i = d i + t i , x i = s i y_i=d_i+t_i,x_i=s_i yi=di+tixi=si
  2. 维护下凸包;
  3. 目标斜率是 X i X_i Xi,单调递增;
  4. 双端队列。

比较基础的一道题目,可以作为模板题使用

WA点:

  1. 计算dp时i和j搞混
  2. 计算斜率时把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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值