題目鏈接:點擊查看
題目大意:給出一個 1 ~ n 的排列 a?,現在規定騷區間當且僅當 a[ l ] 是 [ l , r ] 這段區間內的次小值,同時 a[ r ] 是 [ l , r ] 這段區間內的次大值,現在問有多少個子區間(連續)是sao的
題目分析:一段對兩個端點都有約束的區間,在確定下來一個端點后,根據條件不難確定出另一個端點的可行范圍
但為了確定有多少左右區間可以配對,我們可以利用線段樹和掃描線的思想進行維護
枚舉端點 i 作為左端點時,找到右端點的可行區間 [ l , r ] ,將點 l 的標記打上 { i , 1 } ,點 r + 1 的標記打上 { i , - 1 }?
然后更新點 i 的標記到線段樹上,如果標記的第二個值為 1 ,說明將第一個添加到線段樹上,- 1 代表刪除
最后令點 i 作為右端點,找到左端點的可行區間 [ L , R ] ,在線段樹上統計區間 [ L , R ] 內有多少個數,就說明有多少個數可以在點 i 作為右端點時,作為左端點與其匹配
只是單純的一種可以解決對區間兩個端點存在約束的配對問題的方法,實現起來的話線段樹也可以用樹狀數組代替,時間復雜度 nlogn
代碼:
?
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=1e6+100;vector<int>pos[N];int c[N],cnt[N],a[N],n;int lowbit(int x)
{return x&(-x);
}void update(int pos,int val)
{while(pos<N){c[pos]+=val;pos+=lowbit(pos);}
}int query(int pos)
{int ans=0;while(pos){ans+=c[pos];pos-=lowbit(pos);}return ans;
}struct Node
{int l,r;int mmin,mmax;
}tree[N<<2];void pushup(int k)
{tree[k].mmin=min(tree[k<<1].mmin,tree[k<<1|1].mmin);tree[k].mmax=max(tree[k<<1].mmax,tree[k<<1|1].mmax);
}void build(int k,int l,int r)
{tree[k].l=l;tree[k].r=r;if(l==r){tree[k].mmax=tree[k].mmin=a[l];return;}int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);pushup(k);
}int query_min(int k,int l,int r,int val)//返回區間[l,r]內小于val的最小位置
{if(l>r)return n+1;if(tree[k].l>r||tree[k].r<l)return n+1;if(tree[k].mmin>=val)return n+1;if(tree[k].l==tree[k].r)return tree[k].l;int temp=query_min(k<<1,l,r,val);if(temp!=n+1)return temp;return query_min(k<<1|1,l,r,val);
}int query_max(int k,int l,int r,int val)//返回區間[l,r]內大于val的最大位置
{if(l>r)return 0;if(tree[k].l>r||tree[k].r<l)return 0;if(tree[k].mmax<=val)return 0;if(tree[k].l==tree[k].r)return tree[k].l;int temp=query_max(k<<1|1,l,r,val);if(temp!=0)return temp;return query_max(k<<1,l,r,val);
}int main()
{
#ifndef ONLINE_JUDGE
// freopen("input.txt","r",stdin);
// freopen("output.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d",a+i);build(1,1,n);LL ans=0;for(int i=1;i<=n;i++){int l=query_min(1,i,n,a[i]),r=query_min(1,l+1,n,a[i]);pos[l].push_back(i);pos[r].push_back(-i);for(auto v:pos[i]){if(v>0){if(cnt[v]==0)update(v,1);cnt[v]++;}else{cnt[-v]--;if(cnt[-v]==0)update(-v,-1);}}r=query_max(1,1,i,a[i]),l=query_max(1,1,r-1,a[i]);ans+=query(r)-query(l);}printf("%lld\n",ans);return 0;
}
?
總結
以上是生活随笔為你收集整理的牛客 - 骚区间(线段树+思维)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。