题目大意
有一个环,由n块石头组成。第i块石头有一个高度ai,两块不同的石头i,j能够互相看到,则它们在环上的两条路径中有至少一条路径上除了两个端点(即i,j)路径上石头高度都不大于min(ai,aj)。求有多少对石头能够互相看到。
解题思路
假如在序列上,合法的i,j(i小于j,ai小于aj)一定是i往右第一个比它大的数,这个可以用单调栈做。对于ai==aj一样可以用单调栈求出中间没有比它们大的ai==aj个数。对于环可以在最大值处破开去掉最大值,只有同是最大值才有可能越过破开的点,然而根本不需要,因ai,aj也是最大值。对于破开的点的贡献,一个点往左或往右没有一个比它大的数即可以贡献。
code
#include<set>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
#define LD double
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a>b)?b:a)
#define fo(i,j,k) for(LL i=j;i<=k;i++)
#define fd(i,j,k) for(LL i=j;i>=k;i--)
using namespace std;
LL const inf=2147483647;
LL const maxn=1000000;
LL n,t,v,a[maxn+10],b[maxn+10],top,st[maxn+10],lef[maxn+10],righ[maxn+10],s[maxn+10];
char ch;
LL get(){
v=0;ch=getchar();
for(;(ch<'0')||(ch>'9');ch=getchar());
for(;(ch>='0')&&(ch<='9');v=v*10+ch-'0',ch=getchar());
return v;
}
int main(){
freopen("d.in","r",stdin);
freopen("d.out","w",stdout);
scanf("%lld",&t);
fo(cas,1,t){
scanf("%lld",&n);
LL mx=0,pos,ans=0,tmp=0;
fo(i,1,n){
a[i]=get();
if(a[i]>mx){
mx=a[i];
pos=i;
}
}
fo(i,1,n-1)
b[i]=a[(pos+i>n)?pos+i-n:pos+i];
top=0;
fo(i,1,n-1){
tmp=0;
for(;top&&(b[st[top]]<=b[i]);ans+=(b[st[top]]==b[i])*s[top],tmp+=(b[st[top]]==b[i])*s[top],top--);
if(top){
lef[i]=st[top];
ans++;
}
else lef[i]=0;
st[++top]=i;s[top]=1+tmp;
}
top=0;
fd(i,n-1,1){
for(;top&&(b[st[top]]<=b[i]);top--);
if(top){
righ[i]=st[top];
ans++;
}
else righ[i]=0;
st[++top]=i;
}
fo(i,1,n-1)
if((!lef[i])||(!righ[i]))ans++;
printf("%lld\n",ans);
}
return 0;
}