zoukankan      html  css  js  c++  java
  • [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 }
  • 相关阅读:
    LeetCode 230. Kth Smallest Element in a BST
    LeetCode 114. Flatten Binary Tree to Linked List
    LeetCode 222. Count Complete Tree Nodes
    LeetCode 129. Sum Root to Leaf Numbers
    LeetCode 113. Path Sum II
    LeetCode 257. Binary Tree Paths
    Java Convert String & Int
    Java Annotations
    LeetCode 236. Lowest Common Ancestor of a Binary Tree
    LeetCode 235. Lowest Common Ancestor of a Binary Search Tree
  • 原文地址:https://www.cnblogs.com/halfrot/p/7440794.html
Copyright © 2011-2022 走看看