zoukankan      html  css  js  c++  java
  • BZOJ4709 JSOI2011 柠檬

    Description

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

    Input

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

    Output

    仅一个整数,表示 $Flute$ 最多能得到的柠檬数。
     
    题目大意
    给定一个序列,可以划分成任意的连续段,每一段选一个数,对于每一段,对答案的贡献是该段选的数乘以该数在这一段出现次数的平方,求所有答案之和的最大值。
     
    有一个很显然的结论,对于最后选出来的每一段,一定满足该段两侧的数$=$该段选出来的数。
    那么就可以列出$DP$方程,设$F_i$表示到第$i$位为止划分出的最大值,$C_i$表示从第$1$位开始到第$i$位$s_i$的出现次数。
    $F_i=max{ F_{j-1}+s_i(C_i-C_j+1)^2} (0<jleq i,s_j=s_i)$。
    展开后有$F_i-s_iC_i^2=max{ F_{j-1}-2s_iC_i(C_j-1)+s_j(C_j-1)^2} (0<jleq i,s_j=s_i)$。
    暂且忽略$max$,有$F_i-s_iC_i^2+2s_iC_i(C_j-1)=F_{j-1}+s_j(C_j-1)^2$
    这个东西显然可以斜率优化。
    设$X_j=2(C_j-1),Y_j=F_{j-1}+s_j(C_j-1)^2,b_i=F_i-s_iC_i^2,k_i=s_iC_i$
    那么很显然就有$Y_j=k_iX_j+b_i$,而我们的目的是让截距$b_i$尽可能的较大。
    这个式子在于意义在于斜率为让$k_i$的直线过$(X_j,Y_j)$且截距尽可能大(其中$k_i,X_j,Y_j$均为已知且可求)
    我们发现只有上凸壳的点对答案的贡献才有意义,由于对于每一个$s_i$会单独考虑,那么直接维护上凸壳,因为原来函数是单峰的,就每次在上凸壳上二分出从前往后答案停止增加的位置,求答案即可
     
    献上本机$AC$提交$RE$的代码(本地测$lysdy$数据)
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<vector> 
    #include<cmath>
    #define LL long long
    #define M 200020
    using namespace std;
    LL read(){
        LL nm=0,fh=1; char cw=getchar();
        for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh;
        for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0');
        return nm*fh;
    }
    vector<LL> v[M];
    LL n,m,p[M],c[M],cnt[M],F[M];
    LL sq(LL x){return x*x;}
    LL X(LL i){return (c[i]-1)<<1;}
    LL Y(LL i){return F[i-1]+p[i]*sq((c[i]-1));}
    LL K(LL i){return p[i]*c[i];}
    bool check(LL t1,LL t2,LL t3){return (Y(t3)-Y(t2))*(X(t2)-X(t1))<(Y(t2)-Y(t1))*(X(t3)-X(t2));}
    LL getans(LL pos,LL from){return F[from-1]+p[pos]*sq(c[pos]-c[from]+1);}
    LL fd(LL i,LL tot){
        LL l=0,r=tot-1,res=tot,md;
        for(md=((l+r)>>1);l<=r;md=((l+r)>>1)){
            if(getans(i,v[p[i]][md])<=getans(i,v[p[i]][md+1])) l=md+1;
            else res=md,r=md-1;
        } return res;
    }
    int main(){
        n=read();
        for(LL i=1;i<=n;i++){
            p[i]=read(),c[i]=++cnt[p[i]]; LL tt=v[p[i]].size()-1,now;
            while(tt>0) if(!check(v[p[i]][tt-1],v[p[i]][tt],i)) v[p[i]].pop_back(),tt--;else break;
            tt++,v[p[i]].push_back(i),now=fd(i,tt),F[i]=getans(i,v[p[i]][now]);
        }
        printf("%lld
    ",F[n]); return 0;
    }
     
  • 相关阅读:
    (hdu step 7.1.2)You can Solve a Geometry Problem too(乞讨n条线段,相交两者之间的段数)
    阅读&lt;反欺骗的艺术&gt;思考
    顺序查找(改进)
    win7电脑那些事
    激活office 2010
    MyEclipse10安装SVN插件
    合并排序法
    希尔排序法
    直接插入排序法
    快速排序法——较优方法
  • 原文地址:https://www.cnblogs.com/OYJason/p/9733293.html
Copyright © 2011-2022 走看看