BZOJ 3437 : 斜率优化DP

来源:互联网 发布:sql select 不重复 编辑:程序博客网 时间:2024/04/29 21:18

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;}


原创粉丝点击