【LuoguP4137】区间mex-主席树
测试地址:区间mex
做法:本题需要用到主席树。
看到本题是离线的,很多同学想到了很显然的莫队做法,或者一些奇奇怪怪的离线做法,但实际上在线做法是有的:主席树。
考虑转换条件,我们要求最小的在一个区间中没出现过的自然数,如果把原数列看做往一个空序列中一个一个插入元素,那实际上我们找的就是在某段时间内没有被插入过的最小的元素。考虑按照权值建线段树,对于每个线段树中的区间,我们存储这个区间中最久没有被插入过的点上一次被修改的时间,如果这个时间在所询问的左端点之前,那么就意味着在我们询问的区间里,存在一个在该范围中的元素没有出现过,那么我们就可以这样查找下去找到这个元素了。这个操作显然可以可持久化,于是建一棵主席树就好了。
以下是本人代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,m,len,tot=0,a[200010]={0},low=0,ans,p[200010];
int rt[200010]={0},ch[4000010][2]={0},seg[4000010];
struct forsort
{
int val,id;
}f[200010];
bool cmp(forsort a,forsort b)
{
return a.val<b.val;
}
void pushup(int no)
{
seg[no]=min(seg[ch[no][0]],seg[ch[no][1]]);
}
void build(int &v,int l,int r)
{
if (!v) v=++tot;
if (l==r) {seg[v]=0;return;}
int mid=(l+r)>>1;
build(ch[v][0],l,mid);
build(ch[v][1],mid+1,r);
pushup(v);
}
void insert(int &v,int last,int l,int r,int x,int tim)
{
if (!v)
{
v=++tot;
seg[v]=seg[last];
}
if (l==r) {seg[v]=tim;return;}
int mid=(l+r)>>1;
if (x<=mid) insert(ch[v][0],ch[last][0],l,mid,x,tim),ch[v][1]=ch[last][1];
else insert(ch[v][1],ch[last][1],mid+1,r,x,tim),ch[v][0]=ch[last][0];
pushup(v);
}
void query(int v,int l,int r,int limit)
{
if (seg[v]>=limit) return;
if (l==r) {ans=l;return;}
int mid=(l+r)>>1;
if (!ans&&seg[ch[v][0]]<limit) query(ch[v][0],l,mid,limit);
if (!ans&&seg[ch[v][1]]<limit) query(ch[v][1],mid+1,r,limit);
}
int main()
{
freopen("mex.in","r",stdin);
freopen("mex.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&f[i].val);
f[i].id=i;
}
sort(f+1,f+n+1,cmp);
len=0;
f[0].val=-1;
for(int i=1;i<=n;i++)
{
if (i==1||f[i].val!=f[i-1].val)
{
if (f[i].val>f[i-1].val+1) {low=f[i-1].val+1;break;}
p[++len]=f[i].val;
}
a[f[i].id]=len;
}
if (!low) low=f[n].val+1;
if (len)
{
build(rt[0],1,len);
for(int i=1;i<=n;i++)
{
if (a[i]) insert(rt[i],rt[i-1],1,len,a[i],i);
else rt[i]=rt[i-1];
}
}
for(int i=1;i<=m;i++)
{
int L,R;
scanf("%d%d",&L,&R);
if (!len) printf("0\n");
else
{
ans=0;
query(rt[R],1,len,L);
if (!ans) printf("%d\n",low);
else printf("%d\n",p[ans]);
}
}
return 0;
}