音乐会的等待-单调栈(进阶版)

这道题的基础做法在上一篇博文中已经提到了,详情请见:https://www.cnblogs.com/yufenglin/p/10306366.html

而在课上有人提到了一种简便做法(错的,但可以改对),先来回顾一下题目:

题目描述

N个人正在排队进入一个音乐会。人们等得很无聊,于是他们开始转来转去,想在队伍里寻找自己的熟人。队列中任意两个人A和B,如果他们是相邻或他们之间没有人比A或B高,那么他们是可以互相看得见的。

写一个程序计算出有多少对人可以互相看见。

输入输出格式

输入格式:

输入的第一行包含一个整数N (1 ≤ N ≤ 500 000), 表示队伍中共有N个人。

接下来的N行中,每行包含一个整数,表示人的高度,以毫微米(等于10的-9次方米)为单位,每个人的调度都小于2^31毫微米。这些高度分别表示队伍中人的身高。

输出格式:

输出仅有一行,包含一个数S,表示队伍中共有S对人可以互相看见。

输入输出样例

输入样例#1: 复制

7
2
4
1
2
2
5
1

输出样例#1: 复制

 10

简便做法:不进行二分查找,认为弹出元素的数量加一就是能看到的人数

  乍一看好像是对的:例如 9 8 7 3 2中插入一个6,那么弹出3,2看到了7,3,2,确实是弹出元素数量加一,但是忽略了相等的情况(题中没说不能相等(坑))。

  例如:9,8,7,2中插入一个2,那么显然不会弹出任何元素,但是看到的是两个哇!因此加入特判一。。。

  再例如:φ 中插入任何元素,那么显然答案是0,但是按照上述做法求得答案1!因此加入特判二。。。

  还有很多就不一一列举了,总之,如果一道题加了这么多特判,代码长度一定会大大增加,这里还是提供一种加了补丁后的代码:

  这里用到了两个栈,一个存数,另一个存个数。

 1 #include <cstdio>
 2 #include <stack>
 3 using namespace std;
 4 stack <long long> stk,num;
 5 int n;
 6 long long ans;
 7 int main()
 8 {
 9     scanf("%d",&n);
10     long long tmp;
11     for(int i = 1; i <= n; i++)
12     {
13         scanf("%lld",&tmp);
14         while (!stk.empty() && stk.top() < tmp)
15         {
16             stk.pop();
17             ans += num.top();
18             num.pop();
19         }
20         if (stk.empty())
21         {
22             stk.push(tmp);
23             num.push(1);
24         }
25         else
26         {
27             if (tmp != stk.top())
28             {
29                 ans += 1;
30                 stk.push(tmp);
31                 num.push(1);
32             }
33             else if (stk.size() == 1)
34             {
35                 ans += num.top();
36                 int u = num.top();
37                 num.pop();
38                 num.push(u + 1);
39             }
40             else
41             {
42                 ans += num.top();
43                 int u = num.top();
44                 num.pop();
45                 ans += 1;
46                 num.push(u + 1);
47             }
48         }
49     }
50     printf("%lld\n",ans);
51     return 0;
52 }

接下来是答案的做法:

 1 #include<cstdio>
 2 using namespace std;
 3 int n,top;
 4 long long Ans;
 5 int a[500050],stk[500050];
 6 void dfs(int x)
 7 {
 8     int le=0,ri=top,mid,ret=0;
 9     while(le<=ri)
10     {
11         mid=(le+ri)>>1;
12         if(a[stk[mid]]>x)ret=mid,le=mid+1;
13         else ri=mid-1;
14     }
15     if(!ret)Ans+=top;
16     else Ans+=top-ret+1;
17 }
18 int main()
19 {
20     scanf("%d",&n);
21     for(int i=1; i<=n; ++i)scanf("%d",&a[i]);
22     for(int i=1; i<=n; ++i)
23     {
24         dfs(a[i]);
25         while(top>0&&a[i]>a[stk[top]])--top;
26         stk[++top]=i;
27     }
28     printf("%lld",Ans);
29     return 0;
30 }

那么,答案为了避免上述情况,十分巧妙将两个操作分了开来,并且设置了一个ret值用来判断该栈中元素是否都比插入元素小,从而避免了多种情况的特判。

因此,这道题也算是一个警示:在设计算法是一定要找特殊情况(反数据)来判断算法是否可行。

原文地址:https://www.cnblogs.com/yufenglin/p/10306624.html

时间: 2024-10-19 14:13:52

音乐会的等待-单调栈(进阶版)的相关文章

洛谷P1823 音乐会的等待 单调栈

洛谷P1823 音乐会的等待 单调栈 维护一个上升的单调栈 用以记录有当前这个数向后能看到几个数 但是每次加入一个数 时 他能看到的 是 单调栈中所有比他小的 和跟他一样的数 比他小的下次就没有用了,所以直接退栈 但是 相同的数到后面还是可能会有贡献的,所以贡献算完以后又要进栈 最后如果栈中还有元素,那么栈顶一定能看到自身,所以+1 1 #include <bits/stdc++.h> 2 #define For(i,j,k) for(int i=j;i<=k;i++) 3 #defin

音乐会的等待-单调栈

关于本题,这里只是基础的写法,完美的避开了特殊情况,另一篇博文会详细讲解特殊情况 [COI2007] Patrik 音乐会的等待 题目描述 N个人正在排队进入一个音乐会.人们等得很无聊,于是他们开始转来转去,想在队伍里寻找自己的熟人.队列中任意两个人A和B,如果他们是相邻或他们之间没有人比A或B高,那么他们是可以互相看得见的. 写一个程序计算出有多少对人可以互相看见. 输入输出格式 输入格式: 输入的第一行包含一个整数N (1 ≤ N ≤ 500 000), 表示队伍中共有N个人. 接下来的N行

codeforce1029B B. Creating the Contest(简单dp,简单版单调栈)

B. Creating the Contest time limit per test 1 second memory limit per test 256 megabytes input standard input output standard output You are given a problemset consisting of nn problems. The difficulty of the ii-th problem is aiai. It is guaranteed t

【bzoj3956】Count 单调栈+可持久化线段树

题目描述 输入 输出 样例输入 3 2 0 2 1 2 1 1 1 3 样例输出 0 3 题解 单调栈+可持久化线段树 本题是 bzoj4826 的弱化版(我为什么做题总喜欢先挑难的做QAQ) $k$对点对$(i,j)$有贡献,当且仅当$a_k=max(a_{i+1},a_{i+2},...,a_{r-1})$,且$a_k<a_i\&\&a_k<a_j$. 那么我们可以使用单调栈求出i左面第一个比它大的位置$lp[i]$,和右面第一个比它大的位置$rp[i]$,那么点对$(lp

Terrible Sets_单调栈

Description Let N be the set of all natural numbers {0 , 1 , 2 , . . . }, and R be the set of all real numbers. wi, hi for i = 1 . . . n are some elements in N, and w0 = 0. Define set B = {< x, y > | x, y ∈ R and there exists an index i > 0 such

洛谷——P1823 音乐会的等待

https://www.luogu.org/problem/show?pid=1823 题目描述 N个人正在排队进入一个音乐会.人们等得很无聊,于是他们开始转来转去,想在队伍里寻找自己的熟人.队列中任意两个人A和B,如果他们是相邻或他们之间没有人比A或B高,那么他们是可以互相看得见的. 写一个程序计算出有多少对人可以互相看见. 输入输出格式 输入格式: 输入的第一行包含一个整数N (1 ≤ N ≤ 500 000), 表示队伍中共有N个人. 接下来的N行中,每行包含一个整数,表示人的高度,以毫微

洛谷 P1823 音乐会的等待

题目描述 N个人正在排队进入一个音乐会.人们等得很无聊,于是他们开始转来转去,想在队伍里寻找自己的熟人.队列中任意两个人A和B,如果他们是相邻或他们之间没有人比A或B高,那么他们是可以互相看得见的. 写一个程序计算出有多少对人可以互相看见. 输入输出格式 输入格式: 输入的第一行包含一个整数N (1 ≤ N ≤ 500 000), 表示队伍中共有N个人. 接下来的N行中,每行包含一个整数,表示人的高度,以毫微米(等于10的-9次方米)为单位,每个人的调度都小于2^31毫微米.这些高度分别表示队伍

洛谷 P1823 音乐会的等待 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:https://www.luogu.org/problem/show?pid=1823 题目描述 N个人正在排队进入一个音乐会.人们等得很无聊,于是他们开始转来转去,想在队伍里寻找自己的熟人.队列中任意两个人A和B,如果他们是相邻或他们之间没有人比A或B高,那么他们是可以互相看得见的. 写一个程序计算出有多少对人可以互相看见. 输入输出格式 输入格式: 输入的第一行包含一个整数N (1 ≤ N ≤ 500 000),

(转载)单调栈题目总结

POJ 2796 Feel Good 题意:给出一个长度为n(n<100000)的序列,求出一个子序列,使得这个序列中的最小值乘以这个序列的和的值最大. 思路:枚举每一个点,然后算出以这个点为最小值的区间能向左向右扩展到哪里,然后选择最优的就行. SOJ 3085: windy's cake V 题意:和POJ2796一样,只是不要求输出子序列的两个端点. 思路:粘贴上一题的代码就行 SOJ 3329: Maximum Submatrix II 题意:给出一个n*m的矩阵,求出这个矩阵的最大0矩