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

Solution

首先可以得到 d p dp dp 方程 f [ i ] = m a x ( f [ j ] + a ( s u m [ i ] − s u m [ j ] ) 2 + b ( s u m [ i ] − s u m [ j ] ) + c ) f[i]=max(f[j]+a(sum[i]-sum[j])^2+b(sum[i]-sum[j])+c) f[i]=max(f[j]+a(sum[i]sum[j])2+b(sum[i]sum[j])+c)

f [ i ] = f [ j ] + a ⋅ s u m [ i ] 2 − 2 a ⋅ s u m [ i ] ⋅ s u m [ j ] + a ⋅ s u m [ j ] 2 + b ⋅ s u m [ i ] − b ⋅ s u m [ j ] + c f[i]=f[j]+a\cdot sum[i]^2-2a\cdot sum[i]\cdot sum[j]+a\cdot sum[j]^2+b\cdot sum[i]-b\cdot sum[j]+c f[i]=f[j]+asum[i]22asum[i]sum[j]+asum[j]2+bsum[i]bsum[j]+c

f [ j ] + a ⋅ s u m [ j ] 2 − b ⋅ s u m [ j ] + c = 2 a ⋅ s u m [ i ] ⋅ s u m [ j ] + f [ i ] + a ⋅ s u m [ i ] 2 − b ⋅ s u m [ i ] f[j]+a\cdot sum[j]^2-b\cdot sum[j]+c=2a\cdot sum[i]\cdot sum[j]+f[i]+a\cdot sum[i]^2-b\cdot sum[i] f[j]+asum[j]2bsum[j]+c=2asum[i]sum[j]+f[i]+asum[i]2bsum[i]

然后令点为 ( s u m [ j ] , f [ j ] + a ⋅ s u m [ j ] 2 − b ⋅ s u m [ j ] + c ) (sum[j],f[j]+a\cdot sum[j]^2-b\cdot sum[j]+c) (sum[j],f[j]+asum[j]2bsum[j]+c)

注:题目求的是最大值!要维护上凸包!

Code

#include <cstdio>
#define ll long long
#define db double
const int N=1000010;
int n,a,b,c,q[N];
db sum[N],f[N];
inline db X(int i){return sum[i];}
inline db Y(int i){return f[i]+a*sum[i]*sum[i]-b*sum[i]+c;}
inline db slope(int i,int j){return (Y(i)-Y(j))/(X(i)-X(j));}
int main(){
    scanf("%d%d%d%d",&n,&a,&b,&c);
    for(int i=1;i<=n;i++) {
        scanf("%lf",&sum[i]); 
        sum[i]+=sum[i-1];
    }
    int h=1,t=1;
    for(int i=1;i<=n;i++){
        while(h<t && slope(q[h],q[h+1])>2*a*sum[i]) h++;
        f[i]=f[q[h]]+a*sum[i]*sum[i]-2*a*sum[i]*sum[q[h]]+a*sum[q[h]]*sum[q[h]]+b*sum[i]-b*sum[q[h]]+c;
        while(h<t && slope(q[t],q[t-1])<slope(q[t-1],i)) t--;
        q[++t]=i;
    }
    printf("%.0lf\n",f[n]);
    return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值