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

183 篇文章 0 订阅
13 篇文章 0 订阅

传送门
斜率优化dp经典题。
题目中说的很清楚,设f[i]表示前i个数分配出的最大值。
那么有:
f[i]=max(f[j]+A(sum[i]sum[j])2+B(sum[i]sum[j])+C) 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]+Asum[j]22Asum[i]sum[j]Bsum[j])+Asum[i]2+Bsum[i]+C f [ i ] = m a x ( f [ j ] + A ∗ s u m [ j ] 2 − 2 ∗ A ∗ s u m [ i ] ∗ s u m [ j ] − B ∗ s u m [ j ] ) + A ∗ s u m [ i ] 2 + B ∗ s u m [ i ] + C
同样是比较两个决策k1,k2如果k1优于k2,那么:
f[k1]+Asum[k1]22Asum[i]sum[k1]Bsum[k1] f [ k 1 ] + A ∗ s u m [ k 1 ] 2 − 2 ∗ A ∗ s u m [ i ] ∗ s u m [ k 1 ] − B ∗ s u m [ k 1 ]
>
f[k2]+Asum[k2]22Asum[i]sum[k2]Bsum[k2]) f [ k 2 ] + A ∗ s u m [ k 2 ] 2 − 2 ∗ A ∗ s u m [ i ] ∗ s u m [ k 2 ] − B ∗ s u m [ k 2 ] )
为了让每次查询的斜率单调递增。
我们需要巧妙地移项:
t[k]=f[k]+Asum[k]2Bsum[k] t [ k ] = f [ k ] + A ∗ s u m [ k ] 2 − B ∗ s u m [ k ]
=> (t[k1]t[k2])>2Asum[i](sum[k1]sum[k2]) ( t [ k 1 ] − t [ k 2 ] ) > 2 ∗ A ∗ s u m [ i ] ∗ ( s u m [ k 1 ] − s u m [ k 2 ] )
=> slope(k1,k2)/A<2sum[i] s l o p e ( k 1 , k 2 ) / A < 2 ∗ s u m [ i ]
搞定了。
代码:

#include<bits/stdc++.h>
#define ll long long
#define N 1000005
using namespace std;
inline ll read(){
    ll ans=0,w=1;
    char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
    while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
    return ans*w;
}
int n,q[N],hd,tl;
ll A,B,C,sum[N],f[N];
inline double slope(int i,int j){return 1.0*(sum[i]+sum[j])+(1.0*(f[i]-f[j])/(sum[i]-sum[j])-B)*1.0/A;}
int main(){
    n=read(),A=read(),B=read(),C=read(),hd=tl=1;
    for(int i=1;i<=n;++i)sum[i]=sum[i-1]+read();
    for(int i=1;i<=n;++i){
        while(hd<tl&&slope(q[hd+1],q[hd])<2*sum[i])++hd;
        int j=q[hd];
        f[i]=f[j]+A*(sum[i]-sum[j])*(sum[i]-sum[j])+B*(sum[i]-sum[j])+C;
        while(hd<tl&&slope(i,q[tl])<slope(q[tl],q[tl-1]))--tl;
        q[++tl]=i;
    }
    cout<<f[n];
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值