正题
题目链接:https://ac.nowcoder.com/acm/contest/7329/E
题目大意
给出一个nnn的排列,求有多少个区间[l,r][l,r][l,r]使得最大值是rrr,最小值是lll。
解题思路
首先对于一个位置的值作为左端点和右端点都有一段合法区间(到左边第一个比他小的和右边第一个比他大的,当右端点时同理)。可以用树状数组预处理每个的合法区间
然后对于两个点各作为左右端点需要满足左端点在右端点的合法区间内,右端点在左端点的合法区间内。
那么有算法就是对于每个右端点我们在他合法区间的左边压入,右边弹出然后指针扫描作为左端点的值进行区间查询即可。
时间复杂度O(nlogn)O(n\log n)O(nlogn)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define lowbit(x) (x&-x)
using namespace std;
const int N=1e6+10;
int n,p[N],la[N],ra[N],lp[N],rp[N],num[N];
vector<int> ql[N],qr[N];
long long ans;
struct Tree_Array{int t[N];void Change(int x,int val){while(x<=n){t[x]=max(val,t[x]);x+=lowbit(x);}return;}int Ask(int x){int ans=0;while(x){ans=max(t[x],ans);x-=lowbit(x);}return ans;}
}Ta,Tp;
struct tTree_Array{int t[N];void Change(int x,int val){while(x<=n){t[x]+=val;x+=lowbit(x);}return;}int Ask(int x){int ans=0;while(x){ans+=t[x];x-=lowbit(x);}return ans;}
}T;
bool cmp(int x,int y)
{return p[x]<p[y];}
int main()
{scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d",&p[i]),num[i]=i;for(int i=1;i<=n;i++){la[i]=Ta.Ask(p[i]);ra[i]=Tp.Ask(n-p[i]+1);Ta.Change(p[i],i);Tp.Change(n-p[i]+1,i);}memset(Ta.t,0,sizeof(Ta.t));memset(Tp.t,0,sizeof(Tp.t));for(int i=n;i>=1;i--){lp[i]=n-Ta.Ask(p[i])+1;rp[i]=n-Tp.Ask(n-p[i]+1)+1;Ta.Change(p[i],n-i+1);Tp.Change(n-p[i]+1,n-i+1);}for(int i=1;i<=n;i++)if(p[i]>=i&&ra[i]<p[i]&&p[i]<rp[i]){ql[ra[i]].push_back(i);qr[rp[i]].push_back(i);}sort(num+1,num+1+n,cmp);for(int i=0;i<=n;i++){int x=num[i];for(int j=0;j<qr[i].size();j++)T.Change(p[qr[i][j]],-1);if(p[x]<=x&&la[x]<p[x]&&p[x]<lp[x])ans+=T.Ask(lp[x]-1)-T.Ask(x-1);for(int j=0;j<ql[i].size();j++)T.Change(p[ql[i][j]],1);}printf("%lld",ans);
}