bzoj1911 [Apio2010]特别行动队(斜率优化)

版权声明:本文为博主原创文章,未经博主允许不得转载,除非先点了赞。 https://blog.csdn.net/A_Bright_CH/article/details/82883843

题目

bzoj 1911 [Apio2010]特别行动队

题解

斜率优化DP
设f[i]表示前i个士兵最大战斗力之和,有
f[i]=\max_{0\leqslant j<i}\left{f[j]+a(sx[i]-sw[j])^2+b(sx[i]-sx[j])+c\right}

         =\max_{0\leqslant j<i}\left{f[j]+a*sx[i]^2-2a*sw[i]*sw[j]+a*sw[j]^2+b*sx[i]-b*sx[j]+c\right}

去掉max,把仅与j有关的移到一边,另一边放与仅与i有关的或和i、j都有关的,得

f[j]+a*sx[j]^2-b*sx[j]=f[i]-a*sx[i]^2+2a*sx[i]*sx[j]-b*sf[i]-c

                                                 =(2a*sx[i])*sx[j]+f[i]-a*sx[i]^2-b*sf[i]-c

根据上式,斜率为2a*sx[i],决策点坐标为(sx[j],f[j]+a*sx[j]-b*sx[j]),截距为f[i]-a*sx[i]^2-b*sf[i]-c


因为要求截距最大,所以我们要维护一个上凸壳,即斜率递减。这点用来维护队尾。
考虑问题斜率,是递减的,所以一个小于当前问题斜率的斜率是不优秀的,根据这一点可以删除队头。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=1e6+10;

int n;
ll a,b,c;
int x[maxn];ll sx[maxn];
ll f[maxn];
int q[maxn];int l,r;

double calc(int j1,int j2)
{
    return (double)((f[j1] + a*sx[j1]*sx[j1] - b*sx[j1]) - (f[j2] + a*sx[j2]*sx[j2] - b*sx[j2])) / (sx[j1] - sx[j2]);
}

int main()
{
    scanf("%d",&n);
    scanf("%lld%lld%lld",&a,&b,&c);
    for(int i=1;i<=n;i++) scanf("%d",&x[i]),sx[i]=sx[i-1]+x[i];
    f[0]=0;
    q[l=r=0]=0;
    for(int i=1;i<=n;i++)
    {
        while(l<r && calc(q[l],q[l+1])>=2*a*sx[i]) l++;
        int j=q[l];
        f[i]=f[j] + a*sx[i]*sx[i] -2*a*sx[i]*sx[j] + a*sx[j]*sx[j] + b*sx[i] - b*sx[j] + c;
        while(l<r && calc(q[r-1],q[r])<=calc(q[r],i)) r--;//维护斜率递减
        q[++r]=i;
    }
    printf("%lld\n",f[n]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/A_Bright_CH/article/details/82883843