题目描述
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
思路:
这道题可以使用单调栈解决。
首先,我们整理一下思路,对于队伍中的一个人来说,他可能看到自己前面的人,也可能看到自己后面的人,但是如果把前后两种情况都考虑的话,寻找一遍下来一定有很多重复,所以我们决定只考虑每个人的前或后一侧的情况,这里我们对每个人的前面的人讨论
如果一个人身后存在一个比他高的人,那么他将不会再被看见,因此,可以可根据身高维护一个单调栈,从左向右依次扫描,扫描到 A 时,我们希望栈里保存的是 A 之前,能够和 A 相互看到的人。具体实现时,栈中的元素是自下而上递减的,这就要求我们再加元素时保证栈顶元素大于等于加进的那个元素,可以用二分法求出栈中第一个大于等于输入的元素的值,并把这个元素以上的元素全部弹出,把输入的元素放进栈中。
写代码时一定要注意:
1.第一个元素单独输入,道理很显然
2.由于第一个元素是单独输入的,所以栈中是有一个元素的,初始时把top设为1
3.根据题意,身高相同的人并不会互相遮挡
#include<iostream> using namespace std; int st[500010],n,top=1,ans,m; int main() { cin>>n; int a; cin>>st[1]; for(int i=2;i<=n;i++) { cin>>a; if(st[top]>a) { top++,ans++,st[top]=a; } else { int l=1,r=top; while(l<r) { m=(l+r)>>1; if(r==l+1)m=r; if(a>=st[m])r=m-1; else l=m; } ans+=top-l+1; while(top>0&&st[top]<a)top--; st[++top]=a; } } cout<<ans; }