依据题意,对于每个元素我们需要在其在右边找到第一个大于它的元素,这两个元素之间的元素都是可看见的,统计这些元素个数到答案里即可。
1.单调队列:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int ans;
deque<int>q;
signed main()
{
int n;
cin>>n;
vector<int>a(n+1);
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=n;i;i--)
{
while(q.size()&&a[i]>a[q.front()]) q.pop_front();
if(q.size())
{
ans+=q.front()-i-1;
}
else ans+=n-i;
q.push_front(i);
}
cout<<ans;
}
单调队列在维护最大值的同时,还能有效记录最大值的下标,用来解此题恰到好处。
复杂度:线性阶O(n)
2.st表+二分查询
设i<j,a[j]是a[i]右边第一个大于a[i]的元素,则i<x<j的任意x都满足a[x]<a[i],也就是满足单调性,可以二分右端点来找到j。
#include<bits/stdc++.h>
using namespace std;
const int N=8e4+10;
#define int long long
int f[N][30],a[N],n,ans;
bool check(int l,int r)
{
int k=log2(r-l);
if(f[l][0]>max(f[l+1][k],f[r+1-(1<<k)][k])) return true;
return false;
}
signed main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>f[i][0]; f[n+1][0]=1e18;
for(int j=1;j<=log2(n);j++)
for(int i=1;i<=n+1-(1<<j);i++)
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
for(int i=1;i<=n;i++)
{
int l=i,h=n,mid;
while(l<h)
{
mid=l+h+1>>1;
if(check(i,mid)) l=mid;
else h=mid-1;
}
ans+=l-i;
}
cout<<ans;
}
复杂度O(nlogn)(预处理O(nlogn)+n次查询O(nlogn))。
24/8/15