洛谷P1823 [COI2007] Patrik 音乐会的等待
标签
- 单调栈
简明题意
- 无 n <= 500000
思路
- 题目问有多少对人可以相互看见。这样的题,我们一般直接去考虑在每个人左边且他能看见的人,这样可以避免重复计数。
- 如果所有人的身高都不一样,那么很显然这是一个单调栈。我们维护一个单调递减的栈,设当前考虑的元素为a[i],
- 如果a[i] < top,那么当前的a[i]除了和他相邻的top看不到任何在他前面的人,即cnt++。
- 如果a[i] > top,那么当前的a[i]可以一直往前看,知道有一个人比a[i]大,即 c n t + = 1 + ∑ 前 面 比 他 矮 的 人 数 cnt += 1 + \sum{前面比他矮的人数} cnt+=1+∑前面比他矮的人数
- 注意到这题每个人的身高可能相同,怎么处理呢?分类讨论?
我分类讨论了,然后我死了分类讨论过于麻烦,这里提供一种简单方法:给单调栈存一个pair,一个记录身高,一个记录数量,这样再去写代码,会容易很多
注意事项
- 一定要注意到人的身高可能相同,要用pair存
- 注意溢出
AC代码
#include<cstdio>
#include<stack>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 500000 + 10;
int n, h[maxn];
struct Node
{
int h, num;
Node(int h, int num) : h(h), num(num) { }
};
void solve()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &h[i]);
stack<Node> s;
long long cnt = 0;
for (int i = 1; i <= n; i++)
{
if (s.empty())
{
s.push(Node(h[i], 1));
continue;
}
if (h[i] < s.top().h)
cnt++, s.push(Node(h[i], 1));
else if (h[i] == s.top().h)
{
if (s.size() != 1)
(cnt += (s.top().num + 1)), s.top().num++;
else
cnt += s.top().num, s.top().num++;
}
else
{
while (!s.empty() && h[i] > s.top().h)
(cnt += s.top().num), s.pop();
if (s.empty())
s.push(Node(h[i], 1));
else
{
if (h[i] == s.top().h)
{
if (s.size() != 1)
(cnt += (s.top().num + 1)), s.top().num++;
else
cnt += s.top().num, s.top().num++;
}
else
cnt++, s.push(Node(h[i], 1));
}
}
}
printf("%lld", cnt);
}
int main()
{
solve();
return 0;
}