• 单调栈 && 洛谷 P2866 [USACO06NOV]糟糕的一天Bad Hair Day(单调栈)


    传送门


    这是一道典型的单调栈。


    题意理解

    先来理解一下题意(原文翻译得有点问题)

    其实就是求对于序列中的每一个数i,求出i到它右边第一个大于i的数之间的数字个数c[i]。最后求出和。

    首先可以暴力求解,时间复杂度o(n^2)显然TLE。

    然后就是用单调栈来做。

    单调栈

    单调栈就是维护一个栈,使得栈中的元素是单调的(递增/递减)。

    假设是递减——对于每一个新来的元素,把栈顶大于这个元素的每一个数字全部弹出,最后把这个元素加进去。

    (如果栈为空,直接加入)

    单调栈有什么用呢?

    • 单调递增栈能以o(n)时间复杂度求出左右两边第一个比它比它小的元素。
    1. 进栈元素能入栈的时候,此时栈顶元素一定是第一个左边第一个比进栈元素小的元素。
    2. 栈内元素出栈的时候,此时进栈元素一定是第一个右边第一个比栈顶元素小的元素。
    • 单调递减栈能以o(n)时间复杂度分别求左右两边第一个比它大的元素。
    1. 进栈元素能入栈的时候,此时栈顶元素一定是第一个左边第一个比进栈元素大的元素。
    2. 栈内元素出栈的时候,此时进栈元素一定是第一个右边第一个比栈顶元素大的元素。

    解题思路

     因为这道题求的是最大的元素,所以用单调递减栈。

    第一种方法就是对于每一个元素,求出其右边第一个大于它的元素,最后作差求和。

    第二种更为简单却难以思考的方法是对于每一个即将进栈的数,ans就加上此时(该元素还未进栈)栈内的元素个数。

    为什么呢?

    此时对于栈内的所有元素,一定是呈单调递减的,所以这个即将进栈的元素就能被栈内的元素看到,所以答案加上栈内的元素个数。

    注意事项

    • 用long long,否则会爆。
    • 注意读题,出栈的条件是<=而不是<。
    • while循环&&的s.size()条件必须放在左边。

    AC代码

     1 #include<iostream>
     2 #include<stack>
     3 #include<cstdio> 
     4 using namespace std;
     5 long long ans;
     6 stack<int>s;
     7 int n,now;
     8 int main(){
     9     cin>>n; 
    10     for(int i=1;i<=n;++i)
    11     {
    12         scanf("%d",&now);
    13         while(s.size()&&s.top()<=now) s.pop();
    14         ans+=s.size();
    15         s.push(now);
    16     }
    17     cout<<ans;
    18     return 0;
    19 }
  • 相关阅读:
    [PHP]socket的连接超时 与 读取/写入超时
    [PHP]引用返回与节省内存
    [PHP]实体类基类和序列化__sleep问题
    [PHP]日志处理error_log()函数和配置使用
    [PHP] 使用反射实现的控制反转
    [PHP] debug_backtrace()可以获取到代码的调用路径追踪
    [TCP/IP] TCP的传输连接管理
    [PHP] sys_get_temp_dir()和tempnam()函数报错与环境变量的配置问题
    [PHP] ubuntu下使用uuid扩展获取uuid
    [Linux] host dig nslookup查询域名的DNS解析
  • 原文地址:https://www.cnblogs.com/yinyuqin/p/11729756.html
Copyright © 2020-2023  润新知