牛客 - 骚区间(线段树+思维)

题目链接:点击查看

题目大意:给出一个 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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
 
typedef long long LL;
 
typedef unsigned long long ull;
 
const int inf=0x3f3f3f3f;
 
const int N=1e6+100;

vectorpos[N];

int c[N],cnt[N],a[N],n;

int lowbit(int x)
{
	return x&(-x);
}

void update(int pos,int val)
{
	while(pos>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=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].r0)
			{
				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;
}

 

你可能感兴趣的:(线段树,思维)