[BZOJ3339] Rmq Problem / mex

[BZOJ3339] Rmq Problem / mex

​ 题目链接:Rmq Problem / mex


​ 主席树好题。

​ 这题做法很多,这里介绍一种在线做法。对于每一个点建立一棵权值线段树,维护从1到该点这个区间内权值出现的情况。由于我们想要知道只是 \(l\)\(r\) 区间内每种权值是否出现,而不在意它出现了几次。那么我们在每个点 \(i\) 记录的权值线段树的叶子节点就记录该权值在1到 \(i\) 这个区间内出现的最靠右的坐标。那么对于一个区间 \(l,r\) 来说,如果 \(r\) 这个点的权值线段树中 \(w\) 这个权值出现最靠右的位置大于等于 \(l\) 就说明在 \(l,r\) 这个区间内 \(w\) 这个权值是存在的。对于一颗权值线段树非叶子节点,我们将它的值记录它儿子节点的值的最小值,然后每次查询我们进行一次树上二分就好了。

​ 代码如下:

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5+5;
struct Tree
{
	int ls,rs,v;
}t[MAXN*21];
int rt[MAXN],a[MAXN],tot;
void build(int &k,int l,int r)
{
	k=++tot;
	if(l==r) return ;
	int mid=l+r>>1;
	build(t[k].ls,l,mid);
	build(t[k].rs,mid+1,r);
	return ;
}
int upd(int k,int l,int r,int x,int val)
{
	int oo=++tot;
	t[oo]=t[k];
	if(l==r)
	{
		t[oo].v=val;
		return oo;
	}
	int mid=l+r>>1;
	if(x<=mid) t[oo].ls=upd(t[k].ls,l,mid,x,val);
	else t[oo].rs=upd(t[k].rs,mid+1,r,x,val);
	t[oo].v=min(t[t[oo].ls].v,t[t[oo].rs].v);
	return oo;
}
int query(int k,int l,int r,int rk)
{
	int mid=l+r>>1;
	if(l==r) return l;
	if(t[t[k].ls].v<rk) return query(t[k].ls,l,mid,rk);
	else return query(t[k].rs,mid+1,r,rk);
}
int main()
{
	int n,q;
	scanf("%d %d",&n,&q);
	int Siz=0;
	for(int i=1;i<=n;++i)
	{
		scanf("%d",&a[i]),a[i]++;
		Siz=max(Siz,a[i]);
	}
	++Siz;
	build(rt[0],1,Siz);
	for(int i=1;i<=n;++i)
		rt[i]=upd(rt[i-1],1,Siz,a[i],i);
	while(q--)
	{
		int l,r;
		scanf("%d %d",&l,&r);
		printf("%d\n",query(rt[r],1,Siz,l)-1);
	}
	return 0;
}

总结:一种常用的处理区间内某个权值存在性的方法。

posted @ 2021-08-02 09:56  夜空之星  阅读(37)  评论(0编辑  收藏  举报