P1823
[COI2007] Patrik 音乐会的等待
题目描述
\(n\) 个人正在排队进入一个音乐会。人们等得很无聊,于是他们开始转来转去,想在队伍里寻找自己的熟人。
队列中任意两个人 \(a\) 和 \(b\),如果他们是相邻或他们之间没有人比 \(a\) 或 \(b\) 高,那么他们是可以互相看得见的。
写一个程序计算出有多少对人可以互相看见。
数据规模与约定
对于全部的测试点,保证 \(1\le\) 每个人的高度 \(< 2^{31}\),\(1 \le n \le 5\times 10^5\)。
身为一个普及组选手,对单调栈的利用展现出了普及组的水准,致敬传奇普及老哥 cfm
对于序列上点对统计的问题,一般都是以右端点为计数标准,统计有多少个合法的左端点。这道题也一样:对于 \(b\) 统计有多少 \(a(a<b)\) 使得 \(\max\limits_{j = a+1}^{b - 1}h_j \le \min\{h_a, h_b\}\)
这个式子不好做,把右边拆开讨论一下。
- \(h_a > h_b\),即 \(\max\limits_{j = a + 1}^{b-1}h_j < h_b\)。此时显然 \(a\) 只能是“第一个在 \(b\) 之前且大于 \(b\) 的位置”,容易单调栈出来。
- \(h_a < h_b\)。类似的思考会发现这样的 \(a\) 就会被 \(b\) 给“堵住”,从而让 \(b\) 之后的看不到,那么我们只需要针对 \(b\) 单独统计即可。具体可以利用单调栈。
- \(h_a = h_b\)。在顶统计一次,然后将其合并。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 5e5;
int a[N + 10], c[N + 10], b[N + 10];
struct node {
int val, cnt;
};
int bk[N + 10], fr[N + 10], n;
stack <node> sta;
int main() {
cin >> n;
for(int i = 1; i <= n; i++)
cin >> a[i];
ll sum = 0;
for(int i = 1; i <= n; i++) {
node tmp = (node){a[i], 1};
while(!sta.empty() && sta.top().val <= a[i]) {
if(sta.top().val == a[i]) tmp.cnt += sta.top().cnt;
sum += sta.top().cnt;
sta.pop();
}
if(!sta.empty()) sum++;
sta.push(tmp);
}
cout << sum << endl;
}