mex

mex


题目描述

有一个长度为\(n\)的数组\({a_1,a_2,…,a_n}\)\(m\)次询问,每次询问一个区间内最小没有出现过的自然数。
\(n,m\le 2e5\)
\(a_i\le 1e9\)

输入输出格式

输入格式:

第一行n,m。

第二行为n个数。

从第三行开始,每行一个询问l,r。

输出格式:

一行一个数,表示每个询问的答案。

题目分析:

首先可以发现这个题可以根号算法暴力搞。。。

然后觉得不是很好做。

考虑如果我们只记录每个数最后出现的时候是什么。那么我们在查询的时候直接查询\(R\)这个时刻第一个出现的最后时刻比\(L\)小的就可以了。

但是值域较大,不能直接硬上。

可以进行离散化,但是处理比较麻烦。需要先将所有的\(a[i]\)\(a[i]+1\)都放进离散化的数组里面。同时也需要将\(0\)放入数组。然后进行离散化,之后直接上主席树就可以了。

有一种比较容易的方法就是考虑到如果给出的值大于\(n\)的话,那么它就和没有贡献差不多,所以可以直接无视掉,只计算小于等于\(n\)\(a[i]\)的贡献。这样数组就正常的开就可以了。

另提一下,这个题没有要求强制在线,所以可以对所有询问之后排序线段树搞。

#include <bits/stdc++.h>
using namespace std;
#define mid (l+r>>1)
#define ll long long
# include<algorithm>
const int MAXN=4e5+7;
int a[MAXN],L[MAXN<<5],R[MAXN<<5],st[MAXN<<5],T[MAXN],b[MAXN<<2];
int n,q,size,m;
inline int read()
{
    int x=0,c=1;
    char ch=' ';
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    while(ch=='-')c*=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*c;
}
int update(int pre,int l,int r,int p,int x)
{
    int rt=++size;
    L[rt]=L[pre],R[rt]=R[pre];
    if(l==r){
        st[rt]=x;
        return rt;
    }
    if(p<=mid) L[rt]=update(L[pre],l,mid,p,x);
    else R[rt]=update(R[pre],mid+1,r,p,x);
    st[rt]=min(st[L[rt]],st[R[rt]]);
    return rt;
}
int query(int v,int l,int r,int x)
{
    if(!v||l==r) return b[l];
    if(st[L[v]]<x) return query(L[v],l,mid,x);
    else return query(R[v],mid+1,r,x);
}
int main()
{
    b[++m]=0;
    n=read();q=read();
    for(int i=1;i<=n;i++){
        a[i]=read();
        b[++m]=a[i];b[++m]=a[i]+1;
    }
    sort(b+1,b+m+1);
    m=unique(b+1,b+m+1)-b-1;
    T[0]=++size;
    for(int i=1;i<=n;i++){
        int t=lower_bound(b+1,b+m+1,a[i])-b;
        T[i]=update(T[i-1],1,m,t,i);
    }
    
    while(q--){
        int l=read(),r=read();
        printf("%d\n",query(T[r],1,m,l));
    }
    return 0;
}
posted @ 2018-12-10 16:31  ~victorique~  阅读(252)  评论(0编辑  收藏  举报
Live2D