騷區間
題意:給一個長度爲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;
}