题目大意
有一个神奇的大回环,由n块石头组成。
第i块石头有一个高度ai,两块不同的石头i,j能够互相看到,则它们在环上的两条路径中有至少一条路径上除了两个端点(即i,j)路径上石头高度都不大于min(ai,aj)。
求有多少对石头能够互相看到
数据范围n<=1e6,T<=5,1<=ai<=1e9
先不考虑ai=aj的情况
我们可以把序列加倍,然后用单调栈处理出l[i]和r[i] (分别为i左右两边第一个满足a[j]比a[i]大的位置j) ,也就是只考虑i能看到a值比自己大的,同时如果i左右看到位置在原序列中相同ans只+1否则,这样就不会重复计算了。
ai=aj时,这个讨论起来不会很麻烦,处理l[i]的同时便可一起维护。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
#define ll long long
using namespace std;
const int maxn=2e6+5;
int i,j,n,m,t,a[maxn],l[maxn],r[maxn],f[maxn];ll ans,an[maxn];
void read(int &x){x=0;char c=getchar();while ((c<'0')||(c>'9')) c=getchar();while ((c>='0')&&(c<='9')) x=x*10+c-'0',c=getchar();
}
int main(){freopen("forever.in","r",stdin);freopen("forever.out","w",stdout);read(t);while (t>0){t--;read(n);fo(i,1,n+n) an[i]=0;fo(i,1,n) read(a[i]),f[i]=i;fo(i,n+1,n+n) a[i]=a[i-n];ans=0;fo(i,1,n+n) {j=i-1;while ((j>0)&&(a[j]<=a[i])) {if (a[j]==a[i]) {if (i<=n) f[i]=f[j],an[f[i]]++,ans+=an[f[i]];else{if (j<=n&&f[j]!=i-n) ans+=(ll)(an[f[j]]+1)*(an[f[i-n]]+1);}} j=l[j];}l[i]=j;}fod(i,n+n,1){j=i+1;while ((j<=(n+n))&&(a[j]<=a[i])) j=r[j];r[i]=j;}fo(i,1,n) if (r[i]<=n+n) ans++;fo(i,n+1,n+n) if (l[i]>0){if (r[i-n]!=l[i])ans++;}printf("%lld\n",ans);}
}