洛谷 P1823 [COI2007] Patrik 音乐会的等待
洛谷 P1823 [COI2007] Patrik 音乐会的等待
题目描述
N个人正在排队进入一个音乐会。人们等得很无聊,于是他们开始转来转去,想在队伍里寻找自己的熟人。队列中任意两个人A和B,如果他们是相邻或他们之间没有人比A或B高,那么他们是可以互相看得见的。
写一个程序计算出有多少对人可以互相看见。
输入格式
输入的第一行包含一个整数N (1 ≤ N ≤ 500 000), 表示队伍中共有N个人。
接下来的N行中,每行包含一个整数,表示人的高度,以毫微米(等于10的-9次方米)为单位,每个人的调度都小于2^31毫微米。这些高度分别表示队伍中人的身高。
输出格式
输出仅有一行,包含一个数S,表示队伍中共有S对人可以互相看见。
输入输出样例
输入 #1复制
输出 #1复制
说明/提示
数据制作: @w
2018.6.29添加3组数据
题解:
首先看到这题,一个绝对暴力的思路是一一枚举所有组合,然后对于每一个组合,判断一下这俩人是不是可以互相看见。然后累加ans。
肯定是T了,别想了。
但是我们可以通过刚刚的暴力思路进行画图模拟来得出一些更深刻的结论:显然地,如果从某一个人开始向右查找,碰到一群比他矮的,那么这些比他矮的和他自己就是合法的对。但是如果突然碰到一个比他高的,那即使后面还有若干的人比他矮,那也GG,因为这个高的阻挡住了所有的视线。
恍然发现要维护一个具有单调性的队列。于是我们考虑单调队列/单调栈,这道题用单调队列是不行的,因为单调队列是一个先进先出的数据结构,显然和本题题意不符。所以我们用单调栈再考虑一下:我们从队列的第一个人开始,维护一个单调递减的栈,当一个新的元素入栈的时候,显然,他可以看到当前在栈中所有比他矮的人,那就开始按单调栈的操作逐一弹栈,然后一直更新答案。这道题的思路就出来了。
如此写完,WA。
原因有两个:
第一个,如果碰到和其身高相等的咋办?
新开一个变量来记录有几个(包括自己)相等的。同时累计答案,最后再把所有相等的压回去即可。
第二个,注意,如果比他矮的和相等的都弹掉之后,栈还不为空,那么还要加上1.为什么呢?因为如果这个人是比他高的第一个人,那么他俩还是可以互相看到的,而这个人后面的就不行了。
开写,TLE三个点(超级数据)
为啥子会T呢?
这三个数据之所以区别于其他的数据,就是因为特殊构造了很多相等的人。按照我们刚才的思路,统计完了再一一压回去,实在是太慢!所以我们考虑在弹栈的时候累计,最后一起压回去。具体是,开一个二元组,第一维统计身高,第二维统计这个身高的人数,这样在每次弹栈的时候就可以搞很多个人,在某种程度上(可能只针对这3组故意构造的数据)优化了时间。
代码:
#include<stack>
#include<cstdio>
using namespace std;
typedef pair<int,int>par;
typedef long long ll;
ll ans;
int n;
stack<par>s;
int main()
{
scanf("%d",&n);
while(n--)
{
int x;
scanf("%d",&x);
par p=make_pair(x,1);
while (!s.empty()&&x>=s.top().first)
{
if (s.top().first==x)
p.second+=s.top().second;
ans+=s.top().second;
s.pop();
}
if (!s.empty())
ans++;
s.push(p);
}
printf("%lld",ans);
return 0;
}