题意:有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; }