洛谷 P4137 Rmq Problem /mex 解题报告

P4137 Rmq Problem /mex

题意

给一个长为\(n(\le 10^5)\)的数列\(\{a\}\),有\(m(\le 10^5)\)个询问,每次询问区间的\(mex\)


可以莫队然后对值域分块,这样求\(mex\)的复杂度就正确了

一种更优的做法是按值域建可持久化线段树,对每个节点维护当前值域区间的最小出现位置,然后查询的时候就从\(r\)的那棵树一直尽量往左边走就好了


Code:

#include <cstdio>
#include <cstring>
const int N=2e5+10;
#define ls ch[now][0]
#define rs ch[now][1]
#define ols ch[las][0]
#define ors ch[las][1]
int mi[N*30],ch[N*30][2],root[N],n,m,tot;
int min(int x,int y){return x<y?x:y;}
void rebuild(int las,int &now,int l,int r,int p,int d)
{
    now=++tot;
    if(l==r){mi[now]=d;return;}
    int mid=l+r>>1;
    if(p<=mid) rebuild(ols,ls,l,mid,p,d),rs=ors;
    else ls=ols,rebuild(ors,rs,mid+1,r,p,d);
    mi[now]=min(mi[ls],mi[rs]);
}
int query(int now,int l,int r,int lim)
{
    if(l==r) return l;
    int mid=l+r>>1;
    if(mi[ls]<lim) return query(ls,l,mid,lim);
    else return query(rs,mid+1,r,lim);
}
void build(int &now,int l,int r)
{
    mi[now=++tot]=0;
    if(l==r) return;
    int mid=l+r>>1;
    build(ls,l,mid),build(rs,mid+1,r);
}
int main()
{
    scanf("%d%d",&n,&m);
    memset(mi,0x3f,sizeof mi);
    build(root[0],1,++n);
    for(int a,i=1;i<n;i++)
    {
        scanf("%d",&a);
        if(a<n) rebuild(root[i-1],root[i],1,n,a+1,i);
        else root[i]=root[i-1];
    }
    for(int l,r,i=1;i<=m;i++)
    {
        scanf("%d%d",&l,&r);
        printf("%d\n",query(root[r],1,n,l)-1);
    }
    return 0;
}

2019.1.28

posted @ 2019-01-28 21:27  露迭月  阅读(213)  评论(0编辑  收藏  举报