Luogu8094 solution
Problem
给定一个正整数序列 \(a\),问有多少个 \((i,j)\),满足 \(\max\limits_{i<k<j}(a_k)<\min(a_i,a_j)\)。
link->https://www.luogu.com.cn/problem/P8094
Solution
个人认为这是一道经典的单调栈练习题(然而我一眼竟然没有看出来。
为什么是单调栈呢?
暴力的 \(O(n^2)\) 思路是对于每一个 \(i\),循环对大于 \(i\) 的 \(j\),看看 \(\max\limits_{i<k<j}(a_k)\) 是否小于 \(\min(a_i,a_j)\)。其中对于 \(\max\limits_{i<k<j}(a_k)\) 可以一路比较维护。
int ans=0;
for(int i=1;i<=n;i++) {
int max_val=0;
for(int j=i+1;j<=n;j++) {
if(max_val<min(a[i],a[j]))
ans+=j-i+1;
max_val=max(max_val,a[j]);
}
}
接下来我们考虑只循环 \(j\),维护 \(i\) 的变化。
我们构造一个单调递减的单调栈,每进入一个数 \(a_i\),就向前扫描直到遇见一个 \(s_{top}>a_i\),由于有 \(s_{top}\) 挡着,所以之后的我们全部都看不见。
由于有 \(a_i\) 挡着,所以除了 \(s_{top}\) 以及之前高度 \(>a_i\) 的,剩下的 \(a_{i+1}\) 均看不见。
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+5;
int a[N],n;
int Solve() {
stack<int> s;
int ans=0;
for(int i=1;i<=n;i++) {
while(!s.empty()&&a[s.top()]<a[i]) ans+=i-s.top()+1,s.pop(); //能看见,弹出并累加距离
if(!s.empty()) ans+=i-s.top()+1; //能看见,累加距离
s.push(i); //入栈
}
return ans;
}
signed main() {
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
cout<<Solve();
return 0;
}