BZOJ 3437 : 斜率优化DP

Description

小P在MC里有n个牧场,自西向东呈一字形排列(自西向东用1…n编号),于是他就烦恼了:为了控制这n个牧场,他需要在某些牧场上面建立控制站,每个牧场上只能建立一个控制站,每个控制站控制的牧场是它所在的牧场一直到它西边第一个控制站的所有牧场(它西边第一个控制站所在的牧场不被控制)(如果它西边不存在控制站,那么它控制西边所有的牧场),每个牧场被控制都需要一定的花费(毕竟在控制站到牧场间修建道路是需要资源的嘛~),而且该花费等于它到控制它的控制站之间的牧场数目(不包括自身,但包括控制站所在牧场)乘上该牧场的放养量,在第i个牧场建立控制站的花费是ai,每个牧场i的放养量是bi,理所当然,小P需要总花费最小,但是小P的智商有点不够用了,所以这个最小总花费就由你来算出啦。

Input

第一行一个整数 n 表示牧场数目

第二行包括n个整数,第i个整数表示ai

第三行包括n个整数,第i个整数表示bi

 

Output

只有一行,包括一个整数,表示最小花费

Sample Input

4
2 4 2 4
3 1 4 2

Sample Output

9
样例解释
选取牧场1,3,4建立控制站,最小费用为2+(2+1*1)+4=9。
1<=n<=1000000, 0 < a i ,bi < = 10000


HINT:ai、bi数据范围有误,须用LL.


题解:

先写出转移方程:dp[ i ] = min{ dp[ j ] + ∑( i -k )*b[ k ] ( j+1<=k<=i )} + a[ i ]

拆一下变成:dp[ i ] = min { dp[ j ] + i*∑b[ k ] - ∑k*b[ k ] (j+1<=k<=i )} + a[ i ]

显然我们需要维护两个前缀和 sum1[ i ] = ∑b[ j ] ( 1<=j<=i )  sum2[ i ] = ∑j*b[ j ] ( 1<=j<=i )

进而方程变为:dp[ i ] = min { dp[ j ] + i*( sum1[ i ]-sum1[ j ] ) - ( sum2[ i ] - sum2[ j ] )} + a[ i ]

然后通过推导发现满足斜率不等式的优化条件。于是整体复杂度变成O(n)的


Code:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+100;
typedef long long LL;
LL a[maxn],b[maxn];
LL sum1[maxn],sum2[maxn];
LL dp[maxn];
int q[maxn],l,r;
int n;
inline LL x(int k,int j){
	return sum1[j]-sum1[k];
}
inline LL y(int k,int j){
	return dp[j]+sum2[j]-dp[k]-sum2[k];
}
inline LL getdp(int i,int j){
	return dp[j]+1LL*i*(sum1[i]-sum1[j])-(sum2[i]-sum2[j])+a[i];
}
void input(){
	scanf("%d",&n);
	for (int i=1;i<=n;i++){
		scanf("%lld",a+i);
	}
	for (int i=1;i<=n;i++){
		scanf("%lld",b+i);
	}
	for (int i=1;i<=n;i++){
		sum1[i] = sum1[i-1]+b[i];
		sum2[i] = sum2[i-1]+1LL*i*b[i];
	}
}
void solve(){
	q[r++] =0;
	for (int i=1;i<=n;i++){
		while (l+1<r&&y(q[l],q[l+1])<=i*x(q[l],q[l+1]))l++;
		dp[i] = getdp(i,q[l]);
		while (l+1<r&&y(q[r-2],q[r-1])*x(q[r-1],i)>=y(q[r-1],i)*x(q[r-2],q[r-1]))r--;
		q[r++] = i;
	}
	cout<<dp[n]<<endl;
}
int main(){
	input();
//	init();
	solve();
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值