题意:有N个部落围成一圈,每个部落都有一个高度,如果两个部落之间的部落高度都低于这两个部落的高度,那么这两个部落则互相可见,一个部落从顺时针或逆时针方向看到另一个部落都算互相可见
题解:
单调栈
一个部落从顺时针方向看到与它互相可见的部落,一定是一段连续递增的序列,逆时针同理
这就很像单调栈的维护方式
先把最大的点砍掉,断环为链
然后对于每个点顺时针和逆时针的贡献都用单调栈统计一次
最后再把最大点的贡献算一遍
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll long long
using namespace std;
const int N = 1000010;
int n,mx,top;
int a[N],v[N],stk[N],same[N],bj[N],l[N],r[N];
ll ans;
int gi() {
int x=0,q=1; char ch=getchar();
while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
if(ch=='-') q=-1;
while(ch>='0' && ch<='9') x=10*x+ch-'0',ch=getchar();
return x*q;
}
int main() {
n=gi();
for(int i=1; i<=n; i++) {
a[i]=gi();
if(!mx || a[i]>a[mx]) mx=i;
}
for(int i=mx; i<=n; i++) v[++top]=a[i];
for(int i=1; i<=mx-1; i++) v[++top]=a[i];
top=0;
for(int i=2; i<=n; i++) {
while(top && v[i]>=v[stk[top]]) top--;
l[i]=stk[top];
stk[++top]=i;
}
top=0,stk[top]=n+1;
for(int i=n; i>=2; i--) {
while(top && v[i]>=v[stk[top]]) {
if(v[i]==v[stk[top]]) same[i]=same[stk[top]]+1;
top--;
}
r[i]=stk[top];
stk[++top]=i;
}
for(int i=2; i<=n; i++) {
if(l[i]>0) ans++;
if(r[i]<=n) ans++;
ans+=same[i];
}
mx=0;
for(int i=2; i<=n; i++) {
if(v[i]>=mx) bj[i]=1;
mx=max(mx,v[i]);
}
mx=0;
for(int i=n; i>=2; i--) {
if(v[i]>=mx) bj[i]=1;
mx=max(mx,v[i]);
}
for(int i=2; i<=n; i++) ans+=bj[i];
printf("%lld", ans);
return 0;
}