牛客练习赛66 E-骚区间

骚区间
题意:给一个长度为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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章