BZOJ[4709][Jsoi2011]柠檬 斜率优化

本文详细解析了使用斜率优化技巧解决动态规划问题的方法,并通过一个具体的实例来展示如何运用单调栈实现高效的斜率优化算法。

传送门ber~
容易发现选的每一段左右一定是同色…
那么有 fi=max{fj1+(sisj+1)2ai} f i = m a x { f j − 1 + ( s i − s j + 1 ) 2 ∗ a i }
其中 i i ,j同色
若存在 k>t k > t 且答案更优,即

fk1+(sisk+1)2ai>ft1+(sist+1)2ai f k − 1 + ( s i − s k + 1 ) 2 ∗ a i > f t − 1 + ( s i − s t + 1 ) 2 ∗ a i

fk1ft1>ai(s2i+s2t+12sist2st+2si(s2i+s2k+12sisk2sk+2si)) f k − 1 − f t − 1 > a i ∗ ( s i 2 + s t 2 + 1 − 2 s i s t − 2 s t + 2 s i − ( s i 2 + s k 2 + 1 − 2 s i s k − 2 s k + 2 s i ) )

fk1ft1>ai(s2t2sist2sts2k+2sisk+2sk) f k − 1 − f t − 1 > a i ∗ ( s t 2 − 2 s i s t − 2 s t − s k 2 + 2 s i s k + 2 s k )

fk1ft1>ps2t2psist2pstps2k+2psisk+2psk f k − 1 − f t − 1 > p s t 2 − 2 p s i s t − 2 p s t − p s k 2 + 2 p s i s k + 2 p s k

fk1ft1+ps2kps2t2psk+2pst>si(2psk2pst) f k − 1 − f t − 1 + p s k 2 − p s t 2 − 2 p s k + 2 p s t > s i ( 2 p s k − 2 p s t )

fk1ft1+ps2kps2t2psk+2pst2psk2pst>si f k − 1 − f t − 1 + p s k 2 − p s t 2 − 2 p s k + 2 p s t 2 p s k − 2 p s t > s i

y=fk1+ps2k2psk y = f k − 1 + p s k 2 − 2 p s k
x=2psk x = 2 p s k
fk1+ps2i+ps2k+p2psisk2psk+2psi f k − 1 + p s i 2 + p s k 2 + p − 2 p s i s k − 2 p s k + 2 p s i
=yxsi+ps2i+p+2psi = y − x s i + p s i 2 + p + 2 p s i
斜率优化
单调栈
代码如下·:

#include<algorithm>
#include<cstring>
#include<ctype.h>
#include<cstdio>
#include<vector>
#define int long long
#define INF 2147483647
#define N 100020
using namespace std;
inline int read(){
    int x=0,f=1;char c;
    do c=getchar(),f=c=='-'?-1:f; while(!isdigit(c));
    do x=(x<<3)+(x<<1)+c-'0',c=getchar(); while(isdigit(c));
    return x*f;
}
typedef long long LL;
struct Data{
    LL x,y;
    Data(){}
    Data(LL x,LL y):x(x),y(y){}
}tmp;
vector<Data>s[N];
int n,x,y,p,ans;
int cnt[N],pos[N],a[N],pre[N],top[N],f[N];
inline double Slope(Data a,Data b){
    if(a.x==b.x) return a.y>b.y?-INF:INF;
    return 1.0*(b.y-a.y)/(b.x-a.x);
}
 main(){
    n=read();
    for(int i=1;i<=n;i++){
        a[i]=read();
        pre[i]=pos[a[i]];
        pos[a[i]]=i;
        cnt[i]=cnt[pre[i]]+1;
    }
    memset(pos,0,sizeof pos);
    for(int i=1;i<=n;++i)
        if(!pos[a[i]])
            s[a[i]].push_back(Data(0,0)),pos[a[i]]=1;
    for(int i=1;i<=n;i++){
        p=a[i];
        tmp=Data(2*p*cnt[i],f[i-1]+p*cnt[i]*cnt[i]-2*p*cnt[i]);
        while(top[p]>1 && Slope(s[p][top[p]-1],s[p][top[p]])<=Slope(s[p][top[p]],tmp)) top[p]--,s[p].pop_back();
        top[p]++;s[p].push_back(tmp);
        while(top[p]>1 && Slope(s[p][top[p]-1],s[p][top[p]])<=cnt[i]) top[p]--,s[p].pop_back();
        x=s[p][top[p]].x;y=s[p][top[p]].y;
        f[i]=y-x*cnt[i]+p*cnt[i]*cnt[i]+p+2*p*cnt[i];
        ans=max(ans,f[i]);
    }
    printf("%lld",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值