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;
}
posted @ 2022-08-02 08:52  lsj2009  阅读(29)  评论(0)    收藏  举报