BZOJ1911 [Apio2010]特别行动队 斜率优化

BZOJ1911 [Apio2010]特别行动队 斜率优化

Description

给定一个序列与一个二次函数f(x),将序列连续分段使得所以区间和的二次函数和最大。求最大值。

题解

我们可以很容易想出状态转移方程,s表示前缀和。

f[i]=max(f[i],f[j]F(s[i]s[j]));

可是这样的复杂度有 O(n2)
铁定超时,所以得想办法优化。
因为我们可以看出F(s[i]-s[j])是由i与j决定的而不是单单由i觉定,所以不可以用简单的单调栈优化,但是可以用斜率优化。
我们可以将方程变形
f[i]=f[j]+a(s[i]s[j])2+b(s[i]s[j])+c

f[i]=f[j]+as[i]22as[i]s[j]+as[j]2+bs[i]bs[j]+c

假设 k<j<i ,求出f[i]时,决策j比决策k优,我们有

f[j]+as[i]22as[i]s[j]+as[j]2+bs[i]bs[j]+c>
f[k]+as[i]22as[i]s[k]+as[k]2+bs[i]bs[k]+c

看着不不好看,我们整理得,将s[i]移到右边,
f[j]f[k]+[a(s[j]s[k])b](s[j]s[k])s[j]s[k]>2as[i]

啊吼?
于是不等式就变成左边一个与i,k,相关的分式,也就是斜率,右边是只与i有关的式子。而且惊奇的发现右边是随着i的增大而增大。
为什么可以依靠斜率单调性来进行决策呢?我们记g[i,j]为i,j的决策

同样是 k<j<i ,如果g[i,j]>g[j,k],
假设 g[i,j]>g[j,k]>2as[i],就代表j比k优,i比j优。
假设 2as[i]>g[i,j]>g[j,k],就代表j比i优,k比j优。
无论哪种情况j都不是最优决策,所以j这个决策没必要记录,也就是所有决策中不能存在一个g[i,j]>g[j,k]的情况。
去掉g[i,j]>g[j,k],我们就可以发现决策斜率构成的图像是一个斜率单调递减的队列。
维护好这个队列,对于i的决策就是 第一个满足 g[q[h+1],q[h]]<=2as[i] 的q[h](队头)。因为2as[i]是单调递增,所以可以将排除掉的队头直接出队。
而求出一个f[i]时也要入队,但入队时要维护单调性。

就·这·样,我们可以用斜率优化来进行决策,而且不用二分找,决策复杂度O(1),维护单调队列一共O(n)。

#include <stdio.h>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAXN 1000000+10
#define LL long long
using namespace std;
LL a,b,c,x[MAXN],f[MAXN],sum[MAXN];
int n,q[MAXN],h,t=1;
LL F(LL x) {return x*x*a+b*x+c;}
LL up(int i,int j) {return f[i]-f[j]+((a*(sum[i]+sum[j]))-b)*(sum[i]-sum[j]);}
LL down(int i,int j) {return sum[i]-sum[j];}
int main()
{
    scanf("%d%lld%lld%lld",&n,&a,&b,&c);
    for(int i=1;i<=n;i++) scanf("%lld",&x[i]),sum[i]+=sum[i-1]+x[i];
    f[0]=0;q[1]=0;h=1;t=2;
    for(int i=1;i<=n;i++)
    {   
        while(h<t-1&&up(q[h+1],q[h])>2*a*sum[i]*down(q[h+1],q[h])) h++;
        f[i]=f[q[h]]+F(sum[i]-sum[q[h]]);
        while(h<t-1&&up(q[t-1],q[t-2])*down(i,q[t-2])<up(i,q[t-2])*down(q[t-1],q[t-2]))  t--;
        q[t++]=i;
    }
    printf("%lld\n",f[n]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值