[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;
}
总结:一种常用的处理区间内某个权值存在性的方法。
路漫漫其修远兮,吾将上下而求索。