• [BZOJ4709][JSOI2011]柠檬 决策单调性优化dp


     题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4709

    我好弱啊QAQ,网上dalao们的题解根本看不懂啊,折腾了几个小时,有一点明白了。

    首先要把朴素dp方程退出来。

    ①题目中说每次从序列的左右选一端取,但是如果你真的照着题目说的这样做我也不知道会怎么样。事实上很明显不管怎么取,最终答案都只跟划分出的是哪几个区间有关。所以不妨从左端开始取。

    ②如果取一个区间,区间第一个贝壳的大小和最后一个贝壳的大小不一样,那么很明显可以去掉第一个或最后一个贝壳,把他们加入另一个区间贡献答案,而这一次选取的区间本身答案不会变。于是我们每次取一段区间都可以贪心地来取,使得第一个贝壳和最后一个贝壳大小一定相同。

    有了这两个准则方程很容易就出来了$$f[i]=max{f[j-1]+a[i]*(s[i]-s[j]+1)^2}$$

    其中$s[i]$表示直到第$i$个数$a[i]$出现的次数。

    考虑这个式子中的单调性,可以发现$s[i]$是递增的也就是说$(s[i]-s[j]+1)^2$会增大,而且会增大地越来越快。这就说明如果之前有一个$k<j$满足$k$更优,则$k$会永远比$j$更优。

    于是对于每一个$a[i]$可以用一个单调栈维护,当栈顶第二个元素比第一个元素更优时,弹出就行了,直到结束时,取栈顶元素作为决策。(不弹出)

    但是这样还有一个问题,可以发现,在某些情况下,可能会出现第二个元素劣于第一个元素,但是却第三个元素优于第一个元素,怎么办呢?

    对于任意的$j1<j2<i1<i2$,想一想可以发现如果$j1$超过$i1$的时间小于$j2$超过$i1$的时间,那么$j1$超过$i2$的时间也一定比$j2$超过$i2$的时间早。对于求某一个$j$超过$k$的时间,可以用二分来求。这个时候方法就出来了,在将$i$压入栈之前,我们先判断第二个元素超过$i$的时间是否小于第一个元素超过$i$的时间,如果是就弹栈,直到不满足条件,将$i$压入栈中。

    这样做就满足了每一个元素超过上一个元素的时间也是单调的。

     1 /**************************************************************
     2     Problem: 4709
     3     User: C20161009
     4     Language: C++
     5     Result: Accepted
     6     Time:444 ms
     7     Memory:3024 kb
     8 ****************************************************************/
     9  
    10 #include<cstdio>
    11 #include<cstring>
    12 #include<algorithm>
    13 #include<vector>
    14 using namespace std;
    15 typedef long long ll;
    16 int inline readint(){
    17     int Num;char ch;
    18     while((ch=getchar())<'0'||ch>'9');Num=ch-'0';
    19     while((ch=getchar())>='0'&&ch<='9') Num=Num*10+ch-'0';
    20     return Num;
    21 }
    22 ll f[100010];
    23 int n,a[100010];
    24 int cnt[100010],s[100010];
    25 vector <int> sta[10010];
    26 ll inline cal(int x,int y){
    27     return f[x-1]+(ll)a[x]*y*y;
    28 }
    29 int beyond(int x,int y){
    30     int l=1,r=n,ret=n+1;
    31     while(l<=r){
    32         int mid=l+r>>1;
    33         if(cal(x,mid-s[x]+1)>=cal(y,mid-s[y]+1)){
    34             ret=mid;
    35             r=mid-1;
    36         }
    37         else l=mid+1; 
    38     }
    39     return ret;
    40 }
    41 int main(){
    42     n=readint();
    43     for(int i=1;i<=n;i++){
    44         int x=readint();
    45         a[i]=x;
    46         s[i]=++cnt[x];
    47         while(sta[x].size()>=2&&beyond(sta[x][sta[x].size()-2],sta[x][sta[x].size()-1])<=beyond(sta[x][sta[x].size()-1],i)) sta[x].pop_back();
    48         sta[x].push_back(i);
    49         while(sta[x].size()>=2&&beyond(sta[x][sta[x].size()-2],sta[x][sta[x].size()-1])<=s[i]) sta[x].pop_back();
    50         f[i]=cal(sta[x][sta[x].size()-1],s[i]-s[sta[x][sta[x].size()-1]]+1);
    51     }
    52     printf("%lld
    ",f[n]);
    53     return 0;
    54 }
  • 相关阅读:
    bootstrap 幻灯片(轮播)
    图片轮播
    JS 标签页切换(复杂)
    JS 标签页切换
    js获取日期时间
    HTML标签marquee实现滚动效果
    webstorm 快捷键,很多人想知道的三个常用快捷键
    js红包算法随机分配
    node.js express,ejs后端模板
    递归思路简单例子
  • 原文地址:https://www.cnblogs.com/halfrot/p/7440794.html
Copyright © 2020-2023  润新知