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;
}
对于作者转载文章,欢迎继续转载。
对于作者原创文章,请注明出处之后转载。