bzoj1911 [Apio2010]特别行动队

题目

又是一道非常标准的斜率优化。

一开始还以为需要讨论a来判断是维护上凸包还是下凸包,233。又看了眼数据范围,a一定小于0,嗯,这就轻松多了。

    for(int i=1;i<=n;i++)A[i]=read();
    for(int i=1;i<=n;i++)sum[i]=sum[i-1]+A[i];
    for(int i=1;i<=n;i++)
        for(int j=0;j<i;j++)
            f[i]=max(f[i],f[j]+func(sum[i]-sum[j]));
    cout<<f[n];

n^2转移,再推一推公式,
斜率为
(double)(f[i]-f[j]+b*(sum[j]-sum[i])+a*(sum[i]sum[i]-sum[j]*sum[j]))/(double)(2*a(sum[i]-sum[j])),好长啊

#include<bits/stdc++.h>
#define LL long long
#define N 1000000
using namespace std;
LL n,a,b,c;
LL A[N+1],sum[N+1];
LL f[N+1],l,r,q[N+1];
inline char nc()
{
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline LL read()
{
    LL x=0,f=1;
    char c=nc();
    while((c<'0'||c>'9')&&c!='-')c=nc();
    if(c=='-')f=-1,c=nc();
    while(c<='9'&&c>='0')x=x*10+c-'0',c=nc();
    return x*f;
}
LL func(LL x)
{
    return a*x*x+b*x+c;
}
double slope(int i,int j)
{
    return (double)(f[i]-f[j]+b*(sum[j]-sum[i])+a*(sum[i]*sum[i]-sum[j]*sum[j]))/(double)(2*a*(sum[i]-sum[j]));
}
int main()
{
    n=read();
    a=read(),b=read(),c=read();
    for(int i=1;i<=n;i++)A[i]=read();
    for(int i=1;i<=n;i++)sum[i]=sum[i-1]+A[i];
//  for(int i=1;i<=n;i++)
//      for(int j=0;j<i;j++)
//          f[i]=max(f[i],f[j]+func(sum[i]-sum[j]));
//  cout<<f[n];
    l=1,r=0,q[++r]=0;
    for(int i=1;i<=n;i++)
    {
        while(l<r&&slope(q[l+1],q[l])<=sum[i])l++;
        int tmp=q[l];
        f[i]=f[tmp]+a*(sum[i]-sum[tmp])*(sum[i]-sum[tmp])+b*(sum[i]-sum[tmp])+c;
        while(l<r&&slope(i,q[r])<slope(q[r],q[r-1]))r--;
        q[++r]=i;
    }
    cout<<f[n];
    return 0;
}

貌似还有一种斜率不是单调的,十分毒瘤,要用CDQ分治之类的,我还是不会233

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值