luogu P4137 Rmq Problem / mex

区间求 mex 问题。

对序列建一棵以权值为下标的主席树。每个节点记录:当前区域内每个权值的前一个出现位置的最小值。查询 \([l,r]\) 内的 mex 时拿出 \(r\) 这棵线段树,在上面操作:如果左边区间有最小值小于 \(l\),那么递归到左区间,否则递归进入右区间。

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

const int N=200009;
int n,m,a[N],pool,rt[N];
struct Tree
{
	int l,r,Last;
	#define l(x) b[x].l
	#define r(x) b[x].r
	#define Last(x) b[x].Last
}b[N*32];

void Newpoint(int &k,int last,int l,int r,int x,int qwq)
{
	k=++pool;
	b[k]=b[last];
	if(l==r)
	{
		Last(k)=qwq;
		return;
	}
	int mid=l+r>>1;
	if(mid>=x)
		Newpoint(l(k),l(last),l,mid,x,qwq);
	else
		Newpoint(r(k),r(last),mid+1,r,x,qwq);
	Last(k)=min(Last(l(k)),Last(r(k)));
}

void init()
{
	scanf("%d %d",&n,&m);
	for (int i=1;i<=n;i++)
		scanf("%d",&a[i]),a[i]=a[i]>n?n:a[i];
	for (int i=1;i<=n;i++)
		Newpoint(rt[i],rt[i-1],0,n,a[i],i);
}

int Query(int k,int l,int r,int x)
{
	if(l==r)
		return l;
	int mid=l+r>>1;
	if(Last(l(k))>=x)
		return Query(r(k),mid+1,r,x);
	return Query(l(k),l,mid,x);
}

void work()
{
	int x,y;
	while(m--)
	{
		scanf("%d %d",&x,&y);
		printf("%d\n",Query(rt[y],0,n,x));
	}
}

int main()
{
	init();
	work();
	return 0;
}
posted @ 2020-05-06 22:10  With_penguin  阅读(108)  评论(0编辑  收藏  举报