bzoj-3437 小P的牧场

3437: 小P的牧场
题目链接
Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 1708 Solved: 930
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
Source
KpmCup#0 By Greens

题解
又是斜率优化DP啦 (^o^)/~
这篇也懒得写太多了,如果是刚开始学习的人可以看看我前两篇博客,and 再次推荐ZZK大佬的博客

这题和 仓库建设 很像,拿到转移方程要用前缀和优一道。
f[i]=min{f[j]+a[i]+ik=j+1b[k](ik)} f [ i ] = m i n { f [ j ] + a [ i ] + ∑ k = j + 1 i b [ k ] ∗ ( i − k ) }
等于 min{f[j]+a[i]+iik=j+1b[k]ik=j+1kb[k]} m i n { f [ j ] + a [ i ] + i ∗ ∑ k = j + 1 i b [ k ] − ∑ k = j + 1 i k ∗ b [ k ] }
B[j]=jk=1b[k] B [ j ] = ∑ k = 1 j b [ k ]
SB[j]=jk=1kb[k] S B [ j ] = ∑ k = 1 j k ∗ b [ k ] 不要在意这些细节╮(╯▽╰)╭
那么原式等于 min{f[j]+a[i]+i(B[i]B[j])(SB[i]SB[j])} m i n { f [ j ] + a [ i ] + i ∗ ( B [ i ] − B [ j ] ) − ( S B [ i ] − S B [ j ] ) }

接下来安套路出牌就好了 [手动滑稽]

k<j<i,选 k 不如选 j
f[j]+a[i]+i(B[i]B[j])(SB[i]SB[j])>f[k]+a[i]+i(B[i]B[k])(SB[i]SB[k]) f [ j ] + a [ i ] + i ∗ ( B [ i ] − B [ j ] ) − ( S B [ i ] − S B [ j ] ) > f [ k ] + a [ i ] + i ∗ ( B [ i ] − B [ k ] ) − ( S B [ i ] − S B [ k ] )

[一键移项]
(f[j]f[k]+SB[j]SB[k])/(B[j]B[k])<i ( f [ j ] − f [ k ] + S B [ j ] − S B [ k ] ) / ( B [ j ] − B [ k ] ) < i

[一键判断]
g(i,j)=<i g ( i , j ) = 上 述 式 子 < i
当前修正 f[t] 且 k<j<i<t。
队首:对于 j<i,直接判断 g(i,j)<t g ( i , j ) < t 是否成立。
队尾:对于,那么 g(i,j)<=g(j,k) g ( i , j ) <= g ( j , k ) 时 j 一定是没有用的。

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
#define xie(j,k) ((f[j]-f[k]+sb[j]-sb[k])*1.0/(b[j]-b[k]))
using namespace std;
const int maxn=1e6+5;
int n,a[maxn],q[maxn],til,hea;
LL f[maxn],b[maxn],sb[maxn];
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d",a+i);
    for (int i=1;i<=n;i++)
    {
        scanf("%lld",b+i);
        sb[i]=sb[i-1]+b[i]*i;
        b[i]+=b[i-1];
    }
    for (int i=1,j;i<=n;i++)
    {
        while (hea<til&&xie(q[hea],q[hea+1])<i) hea++;j=q[hea];
        f[i]=f[j]+a[i]+(b[i]-b[j])*i-(sb[i]-sb[j]);
        while (hea<til&&xie(i,q[til])<=xie(q[til],q[til-1])) til--;
        q[++til]=i;
    }
    printf("%lld",f[n]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值