牛客練習賽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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章