首页 > 代码库 > bzoj4709 [jsoi2011]柠檬

bzoj4709 [jsoi2011]柠檬

Description

Flute 很喜欢柠檬。它准备了一串用树枝串起来的贝壳,打算用一种魔法把贝壳变成柠檬。贝壳一共有 N (1 ≤ N
 ≤ 100,000) 只,按顺序串在树枝上。为了方便,我们从左到右给贝壳编号 1..N。每只贝壳的大小不一定相同,
贝壳 i 的大小为 si(1 ≤ si ≤10,000)。变柠檬的魔法要求,Flute 每次从树枝一端取下一小段连续的贝壳,并
选择一种贝壳的大小 s0。如果 这一小段贝壳中 大小为 s0 的贝壳有 t 只,那么魔法可以把这一小段贝壳变成 s
0t2 只柠檬。Flute 可以取任意多次贝壳,直到树枝上的贝壳被全部取完。各个小段中,Flute 选择的贝壳大小 s
0 可以不同。而最终 Flute 得到的柠檬数,就是所有小段柠檬数的总和。Flute 想知道,它最多能用这一串贝壳
变出多少柠檬。请你帮忙解决这个问题。

Input

第 1 行:一个整数,表示 N。
第 2 .. N + 1 行:每行一个整数,第 i + 1 行表示 si。

Output

仅一个整数,表示 Flute 最多能得到的柠檬数。
对位置i,f[i]表示考虑前i个元素的最多柠檬数
f[0]=1
f[i]=max f[j-1]+a[i]*(j到i之间si出现次数)^2 , 1<=j<=i,si=sj
对每个si分别用单调栈维护斜率优化dp,决策不单调但可以三分求出决策点
#include<cstdio>#include<vector>typedef long long i64;const int R=5000000,N=100007;char buf[R+3],*ptr=buf-1;int _(){    int x=0,c=*++ptr;    while(c<48)c=*++ptr;    while(c>47)x=x*10+c-48,c=*++ptr;    return x;}int n,a[N],pv[N],pw[10007],c[N];i64 f[N];std::vector<int>q[10007];void maxs(i64&a,i64 b){if(a<b)a=b;}i64 p2(i64 x){return x*x;}double F(int x,int y,int ci,int ai){    return (f[x-1]-f[y-1]+ai*(p2(c[x])-p2(c[y])))/double(i64(c[x]-c[y])*ai<<1);}int main(){    fread(buf,1,R,stdin);    n=_();    for(int i=1;i<=n;++i){        pv[i]=pw[a[i]=_()];        c[i]=c[pw[a[i]]]+1;        pw[a[i]]=i;    }    for(int i=1;i<=n;++i){        int w=a[i],ci=c[i]+1;        std::vector<int>&Q=q[w];        while(Q.size()>1&&F(i,Q[Q.size()-1],ci,w)>F(Q[Q.size()-1],Q[Q.size()-2],ci,w))Q.pop_back();        Q.push_back(i);        int l=0,r=Q.size()-1;        while(l+4<=r){            int m1=l+r>>1,m2=m1+r>>1;            if(f[Q[m1]-1]+w*p2(ci-c[Q[m1]])<f[Q[m2]-1]+w*p2(ci-c[Q[m2]]))l=m1;            else r=m2;        }        for(int p=l;p<=r;++p)maxs(f[i],f[Q[p]-1]+w*p2(ci-c[Q[p]]));    }    printf("%lld",f[n]);    return 0;}

 

 

bzoj4709 [jsoi2011]柠檬