牛客 - 騷區間(線段樹+思維)

題目鏈接:點擊查看

題目大意:給出一個 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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章