【动态规划24】bzoj3437小P的牧场(dp+斜率优化)

题目描述

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

输入输出格式

第一行一个整数 n 表示牧场数目
第二行包括n个整数,第i个整数表示ai
第三行包括n个整数,第i个整数表示bi
只有一行,包括一个整数,表示最小花费

f[i]表示在i建站的最小花费。
f[i]=min(f[j]+ik=j+1b[k](ik)+a[i]) (j<i)
考虑前缀优化
右式可变为 f[j]+ik=j+1(b[k]ib[k]k)
那么预处理b[j]与b[j]*j前缀
那么该式可变为(b[]为原b[]的前缀和,pre[j]为b[j]*j的前缀和)
f[j]+(b[i]b[j])i(pre[i]pre[j])+a[i]
j<k 且且k优于j
f[j]+(b[i]b[j])i(pre[i]pre[j])+a[i]>f[k]+(b[i]b[k])i(pre[i]pre[k])+a[i]
f[j]b[j]i+pre[j]>f[k]b[k]i+pre[k]
(b[k]b[j])i>f[k]f[j]+pre[k]pre[j]
i> f[k]f[j]+pre[k]pre[j]b[k]b[j]

#include<bits/stdc++.h>
#define ll long long
#define pa pair<int,int>
#define fer(i,j,n) for(int i=j;i<=n;i++)
#define far(i,j,n) for(int i=j;i>=n;i--)
const int INF=1e9;
const int maxn=1000010;
using namespace std;
/*----------------------------------------------------*/
inline ll read()
{
    char ls;int x=0,sng=1;
    for(;ls<'0'||ls>'9';ls=getchar())if(ls=='-')sng=-1;
    for(;ls>='0'&&ls<='9';ls=getchar())x=x*10+ls-'0';
    return x*sng;
}
/*----------------------------------------------------*/
ll a[maxn],b[maxn],pre[maxn];
ll f[maxn],q[maxn];
int n;
ll calcK(int j,int k)
{
    return (f[k]-f[j]+pre[k]-pre[j])/(b[k]-b[j]);
}
int main()
{
    n=read();
    memset(b,0,sizeof(b));
    memset(pre,0,sizeof(pre));
    fer(i,1,n)
        a[i]=read();
    fer(i,1,n)
    {
        b[i]=read();
        pre[i]=b[i]*i+pre[i-1];
        b[i]+=b[i-1];
    }
    int h=0,t=0;
    fer(i,1,n)
    {
        while(h<t&&calcK(q[h],q[h+1])<i)h++;
        f[i]=f[q[h]]+(b[i]-b[q[h]])*i-(pre[i]-pre[q[h]])+a[i];
        while(h<t&&calcK(q[t],q[t-1])>calcK(i,q[t]))t--;
        q[++t]=i;
    }
    cout<<f[n];
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值