【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;
}
posted @ 2018-03-28 17:56  Maxwei_wzj  阅读(166)  评论(0编辑  收藏  举报