zoukankan      html  css  js  c++  java
  • [BZOJ4907]柠檬

    4709: [Jsoi2011]柠檬

    Time Limit: 10 Sec  Memory Limit: 128 MB

    Description

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

    Input

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

    Output

    仅一个整数,表示 Flute 最多能得到的柠檬数。

    Sample Input

    5
    2
    2
    5
    2
    3

    Sample Output

    21
    HINT:Flute 先从左端取下 4 只贝壳,它们的大小为 2, 2, 5, 2。选择 s0 = 2,那么这一段
    里有 3 只大小为 s0 的贝壳,通过魔法可以得到 2×3^2 = 18 只柠檬。再从右端取下最后一
    只贝壳,通过魔法可以得到 1×3^1 = 3 只柠檬。总共可以得到 18 + 3 = 21 只柠檬。没有
    比这更优的方案了。
     
    题解
    膜一波ZYFdalao
    首先我们需要分析出一个性质:同一段被取下的贝壳,其第一个和最后一个的大小一定是一样的.
    证明很容易.用反证法来看,如果有一段贝壳,开头和结尾不一样,显然把这两段分开施法更优
    这样,我们又可以想出一个暴力:每一个节点的最大价值f[i],肯定是由前面某一个和他权值相同的点转移而来
    设出现次数为s[i],权值为a[i]
    显然我们可以写出暴力式子:f[i]=max{f[j-1]+a[i]*(s[i]-s[j]+1)2,a[i]==a[j]}
    但是这样肯定会T的,所以我们考虑优化
    对于两个决策点j1和j2,设j1<j2
    如果一开始f[j1-1]+a[i]*(s[i]-s[j1]+1)2<f[j2-1]+a[i]*(s[i]-s[j2]+1)2
    随着i不断向后,由于平方的增速很快,越靠前的j位置,越有可能反超,使得f[j1-1]+a[i']*(s[i']-s[j1]+1)2>f[j2-1]+a[i']*(s[i']-s[j2]+1)2
    所以我们就不能再用单调队列了,因为如果我们删去了前面的某个点,它在后面可能会变得更优
    于是我们考虑用单调栈维护这个东西(单调栈的实现方法随意,我个人使用了vector)
    每次决策的时候,我们就用栈顶来更新
    由于一个贝壳可以自己画为一段,所以应该在插入之后再计算f值
    当上述"反超"的情况出现时,肯定是最先发生在栈顶和栈顶第二个元素
    假设我们已经插入因此我们就看,第二个元素有"多快"超过栈顶
    这个所谓的"速度"用经过a[i]的个数来体现,也就是说,看第二个元素经过几个a[i]超过栈顶,记为cnt1,如果cnt1<=s[i],我们就可以弹栈了.
    但是,可能会出现一种情况:第三个元素超过栈顶,而第二个元素还没有超过栈顶
    设j2<j1<i2<i1,如果j2,j1,i2三者满足这种情况,那么j2,j1,i1肯定更加满足这种情况,所以直接删掉j1就行
    所以,在我们插入之前,可以先进行一波上述弹栈:如果发现第二个元素超过栈顶比栈顶超过i快,就弹栈;
    所以最后的顺序是:排查->插入->计算
    代码见下:
     1 #include<cstdio>
     2 #include<cstring>
     3 #include<vector>
     4 #include<deque>
     5 #include<algorithm>
     6 using namespace std;
     7 typedef long long LL;
     8 const int N=100010;
     9 const int V=10010;
    10 LL a[N],f[N];
    11 int id[N],cnt[V],n;
    12 vector<int>q[V];
    13 inline int max(int a,int b){return a>b?a:b;}
    14 inline LL calc(int x,int y){return f[x-1]+a[x]*y*y;}
    15 inline int k(int x,int y)
    16 {
    17     int le=1,ri=n,ret=n+1;
    18     while(le<=ri)
    19     {
    20         int mi=(le+ri)>>1;
    21         if(calc(x,mi-id[x]+1)>=calc(y,mi-id[y]+1))
    22             ret=mi,ri=mi-1;
    23         else le=mi+1;
    24     }
    25     return ret;
    26 }
    27 int main()
    28 {
    29     scanf("%d",&n);int o;
    30     for(int i=1;i<=n;i++)
    31     {
    32         scanf("%lld",&a[i]);o=a[i];
    33         cnt[o]++,id[i]=cnt[o];
    34         while(q[o].size()>=2&&k(q[o][q[o].size()-2],q[o][q[o].size()-1])<=k(q[o][q[o].size()-1],i))q[o].pop_back();
    35         q[o].push_back(i);
    36         while(q[o].size()>=2&&k(q[o][q[o].size()-2],q[o][q[o].size()-1])<=id[i])q[o].pop_back();
    37         f[i]=calc(q[o][q[o].size()-1],id[i]-id[q[o][q[o].size()-1]]+1);
    38     }
    39     printf("%lld",f[n]);
    40 }
    BZOJ4907
    Progress is not created by contented people.
  • 相关阅读:
    efibootmgr命令
    GPT+UEFI双系统引导
    关于元类的一些使用心得
    队列的数组实现
    队列的链表实现
    栈的链表实现
    栈的数组实现
    单链表实现基数排序
    多项式ADT加法乘法——单链表实现
    LeetCode: 476 Number Complement(easy)
  • 原文地址:https://www.cnblogs.com/LadyLex/p/7016771.html
Copyright © 2011-2022 走看看