骚区间
题意:给一个长度为n的全排列,求有多少个骚区间。骚区间定义,对于[l,r]这个区间满足在这个区间中只有1个数小于al,只有1个数大于ar,那么这个区间就为骚区间。n<=1000000,ai数组为全排列。
思路:明显枚举每个端点作为骚区间的左端点,假设我们枚举ai作为左端点,然后在他右边找到第一个小于ai的数的位置x,然后再在x的右边找第一个小于ai的数的位置y,明显以ai为左端点的骚区间只能在[x,y)这个区间中选择右端点,同理也可以枚举每个数作为右端点,然后找他左边第一个第二个大于ai的数x,y,那么以这个数为右端点的骚区间的左端点只能在(x,y]中选择,这样的话问题就化成了枚举每个点作为左端点,然后得到一个区间,在log的时间复杂度的下,求这个区间中的每个数对应的区间中包含左端点的区间个数。
这个问题是这个题的最关键点,解决方法:对于每个端点x,他对应的右区间为[Rl,Rr),然后就可以枚举每个端点作为右端点,维护一个树状数组,在枚举到Rl的时候在x上加上1,然后在枚举到Rr时候在x上减去1,这样就可以保证在枚举到第i个端点时,可以把这个点当右端点的左端点的位置都在树状数组中体现出来,这样的话就可以解决这个问题了。
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
const int MAX_N=1010000;
int b[MAX_N],rk[MAX_N];
int Rl[MAX_N],Rr[MAX_N],Ll[MAX_N],Lr[MAX_N];
struct node{
int l,r,minl,maxl,lazy;
}a[MAX_N*4];
void update(int k){
//a[k].sum=a[k<<1].sum+a[k<<1|1].sum;
a[k].minl=min(a[k<<1].minl,a[k<<1|1].minl);
a[k].maxl=max(a[k<<1].maxl,a[k<<1|1].maxl);
}
void build(int k,int l,int r){
a[k].l=l;a[k].r=r;a[k].lazy=0;
if(l==r){
a[k].minl=b[l];
a[k].maxl=b[l];
return;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
update(k);
}
int find1(int k,int x){
if(a[k].l==a[k].r)
return a[k].l;
if(a[k<<1].minl<x)
return find1(k<<1,x);
else
return find1(k<<1|1,x);
}
int query1(int k,int l,int r,int x){
if(a[k].l>=l&&a[k].r<=r){
if(a[k].minl>=x)
return -1;
else
return find1(k,x);
}
int mid=(a[k].l+a[k].r)>>1;
if(l<=mid){
int y=query1(k<<1,l,r,x);
if(y!=-1)
return y;
}
if(r>mid){
int y=query1(k<<1|1,l,r,x);
if(y!=-1)
return y;
}
}
int find2(int k,int x){
if(a[k].l==a[k].r)
return a[k].l;
if(a[k<<1|1].maxl>x)
return find2(k<<1|1,x);
else
return find2(k<<1,x);
}
int query2(int k,int l,int r,int x){
if(a[k].l>=l&&a[k].r<=r){
if(a[k].maxl<=x)
return -1;
else
return find2(k,x);
}
int mid=(a[k].l+a[k].r)>>1;
if(r>mid){
int y=query2(k<<1|1,l,r,x);
if(y!=-1)
return y;
}
if(l<=mid){
int y=query2(k<<1,l,r,x);
if(y!=-1)
return y;
}
}
int sum[MAX_N],n;
void add(int p,int x){
while(p<=n){
sum[p]+=x;
p+=p&-p;
}
}
int ask(int p){
int res=0;
while(p){
res+=sum[p];
p-=p&-p;
}
return res;
}
vector<int>ad[MAX_N],dl[MAX_N];
int main(void){
int i,j;
scanf("%d",&n);
for(i=1;i<=n;i++){
scanf("%d",&b[i]);
rk[b[i]]=i;
}
b[0]=n+1;
build(1,0,n+1);
for(i=1;i<=n;i++){
int x=query1(1,i+1,n+1,b[i]);
if(x!=n+1){
Rl[i]=x;
Rr[i]=query1(1,x+1,n+1,b[i]);
ad[Rl[i]].push_back(i);
dl[Rr[i]].push_back(i);
}
x=query2(1,0,i-1,b[i]);
if(x!=0){
Lr[i]=x;
Ll[i]=query2(1,0,x-1,b[i]);
}
}
long long ans=0;
for(i=1;i<=n;i++){
for(j=0;j<ad[i].size();j++){
int y=ad[i][j];
add(y,1);
}
for(j=0;j<dl[i].size();j++){
int y=dl[i][j];
add(y,-1);
}
ans+=(long long)(ask(Lr[i])-ask(Ll[i]));
}
printf("%lld\n",ans);
return 0;
}