题目传送门
首先考虑如何处理这个环
可以先找出最大的数,然后最大的数放在第一位,按顺序依次下派,最后再加上这个最大数,那么就不需要处理冗杂的计算了,直接就可以线性计算
思考如何在这个链上处理
对于一个点i,设它的左边的第一个比i大点为 l e f t i left_i lefti,右边第一个比他大的点为 r i g h t i right_i righti,那么有i的区间就是 l e f t i left_i lefti~ r i g h t i right_i righti(如果在这个区间的左边或者右边还有,那么a[i]一定小于a[ l e f t i left_i lefti]和a[ r i g h t i right_i righti],就不符合了)
有一个问题:如何计算这里面的东西,就比如样例
5 3 1 2 4 5 假如对于4这个点,左边的最大是5,右边的最大也是5,坐标分别是0,5,但是包含4的这个点的区间有4个(4-5,3-4,2-4,1-4)
还有在处理完这个区间的时候,在处理1的时候也是包含的,所以在最后的答案还要/2,问题是这个答案还不好求出
那么想一下如何这一个区间的数的性质,这个区间的除了左端点和右端点,其他的数都比i小,那么在处理这些小的数的时候(设为j,即j= Σ i = l e f t i r i g h t i \Sigma_{i=left_i}^{right_i} Σi=leftirighti,显然 l e f t j left_j leftj和 r i g h t j right_j rightj都不会分别小于和大于 l e f t i left_i lefti和 r i g h t i right_i righti),如果 l e f t j left_j leftj或 r i g h t i right_i righti都没有i,那么i和j一定不能互相的转换,那么如果有,就已经可以包含进去了,那么在计算i的时候只需要+2就行了(即 l e f t i left_i lefti和 r i g h t i right_i righti)
但是还有一个特殊的情况——当中间有数等于i的时候,那么对于这些数,只需要用cnt记录一下即可(注意只需要记录右边的,因为左边在遍历的时候已经计算过了)
关于怎么计算这3个东西,不是栈就行了
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n,a[N],b[N],lft[N],rgh[N];
int cnt[N],sta[N],tot;
int main()
{
scanf("%d",&n);int Max=0,p;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(Max<a[i]){
Max=a[i];p=i;
}
}b[0]=Max;long long sum=0;
for(int i=p+1;i<=n;i++)b[i-p]=a[i];
for(int i=1;i<=p;i++)b[n-p+i]=a[i];
for(int i=0;i<=n;i++){
while(tot&&b[i]>=b[sta[tot]])tot--;
lft[i]=sta[tot];sta[++tot]=i;
}tot=0;
for(int i=n;i+1;i--){
while(tot&&b[i]>=b[sta[tot]]){
if(b[i]==b[sta[tot]])cnt[i]=cnt[sta[tot]]+1;
tot--;
}
rgh[i]=sta[tot];sta[++tot]=i;
if(i!=0)sum+=cnt[i];
}
for(int i=0;i<n;i++)
if(b[i]<b[0]){//有可能最大的有多个
sum+=2;if(lft[i]==0&&rgh[i]==n){
sum--;}
//这两个其实是一样的,只能算一种
}
printf("%lld",sum);
return 0;
}