描述
给定数组a,长度为1e6,保证a是个排列。给出“骚区间”的定义区间的左端为该区间的次小值,右端为该区间的次大值。求给定数组内有多少个“骚区间”。
思路
对每个元素判定其作为左、右端点时,另一端点所允许的区间,例如[2,4,1,5,3,6],元素4作为做为左端点时,右端点区间可以是从1或5;3作为右端点时,左端点区间可以时1或5。
升序遍历下标,若当前下标是一个右端点对应的左端点可行区间
的左端,则把树状数组上该右端点位置+1;若当前下标是一个右端点对应的左端点可行区间
的右端+1位置,则把树状数组上该右端点位置-1。然后,当前下标是左端点时对应的右端点可行区间
的两端,在树状数组上求该区间和。
求每个元素作为左端点时,右端点的可行区间。
从小到大遍历元素,每次把该元素对应下标插入set集合中,然后upper_bound查询大于该元素下标(从小到大遍历的元素,保证此时里面的下标都是小于该元素的下标)的结果,就是大于该元素,且在该元素的右面最靠近的下标,即右端点可行区间
的左端,此时*(迭代器++) - 1,就是右端点可行区间
的右端。
代码
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include<list>
#include<stack>
#include<queue>
#include<vector>
#define cl (k<<1)
#define cr (k<<1|1)
#define Mid ((a[k].l+a[k].r)>>1)
#define mem(a,x) memset(a,x,sizeof(a))
using namespace std;
#define bug printf("???\n")
typedef long long LL;
const int Inf=0x3f3f3f3f;
const double eps=1e-7;
const int maxn=1e6+50;
int n;
int a[maxn], b[maxn];
vector<int> Inc[maxn], Dec[maxn];
int ql[maxn], qr[maxn];
int t[maxn];
int low(int k){return k&(-k);}
void add(int k,int x){
while(k<=n){
t[k] += x;
k += low(k);
}
}
int get(int k){
int ret=0;
while(k>1){
ret += t[k];
k -= low(k);
}
return ret;
}
int sum(int l,int r){
return get(r)-get(l-1);
}
int main()
{
cin>>n;
for(int i=1; i<=n; i++){
scanf("%d",&a[i]);
b[a[i]] = i;
}
set<int>st;
set<int>::iterator it;
for(int i=n; i>=1; i--){
int id=b[i];
st.insert(-id); //从大到小
it = st.upper_bound(-id);
if(it==st.end()) continue;
Dec[-(*it)+1].push_back(id);
// printf("(%d ",-(*it)+1);
it++;
if(it==st.end()){
Inc[1].push_back(id);
// printf("%d - %d\n",1,id);
}
else{
Inc[-(*it)+1].push_back(id);
// printf("%d - %d\n",-(*it)+1,id);
}
}
st.clear();
for(int i=1; i<=n; i++){
int id=b[i];
st.insert(id);
it = st.upper_bound(id);
if(it==st.end()) continue;
ql[id] = *it;
it++;
if(it==st.end()){
qr[id] = n;
}
else
qr[id] = (*it) - 1;
// printf("(%d %d - %d\n",ql[id], qr[id],id);
}
LL ans=0;
for(int i=1; i<=n; i++){
for(int v:Inc[i]) add(v, 1);
for(int v:Dec[i]) add(v, -1);
ans += sum(ql[i], qr[i]);
}
printf("%lld\n",ans);
}