题解 P4137 【Rmq Problem / mex】
看到没有莫队+离散化的做法,我来水一发
首先,我们对原数组进行排序、去重、二分,即离散化,并把离散化用的辅助数组存下来,记为
和普通做法类似,莫队删除时直接对
所以当我们遇到一个在询问区间里出现过的数
讲起来可能有点乱,上核心代码
/*注意,由于a是离散化后的数组,a里存的数字相当于
原数组的相对大小,也就是lsh数组的下标*/
inline void add(int x)
{
if(++cnt[a[x]]==1)
{
if(ans==lsh[a[x]])//同普通做法
{
for(int i=a[x];i<=len;i++)//在离散化数组里寻找答案
if(cnt[i])//我们需要的是在当前区间里出现的数,因为答案要么是0,要么紧跟在区间中出现的数字后面
if(!(lsh[i]+1==lsh[i+1]&&cnt[i+1]))//判断这一位后面有没有紧跟的数字
{
ans=lsh[i]+1;
return;
}
}
}
}
完整代码
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#include<bits/stdc++.h>
using namespace std;
const int N=200005;
#define dd ch=getchar()
inline int read()
{
int x=0;bool f=false;char dd;
while(!isdigit(ch)){f|=ch=='-';dd;}
while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-48;dd;}
return f?-x:x;
}
#undef dd
void write(int x)
{
if(x<0)putchar('-'),x=-x;
if(x>9)write(x/10);
putchar(x%10+48);
}
int n,m,a[N];
int res[N];
int belong[N];
inline void fuck(const int lim)
{
int size=pow(lim,0.5);
for(int i=1;i<=lim;i++)
belong[i]=(i-1)/size+1;
}
int lsh[N];
int cnt[N];
int ans=0,len;
struct node
{
int l,r,id;
}q[N];
inline bool cmp(node x,node y){return belong[x.l]^belong[y.l]?belong[x.l]<belong[y.l]:x.r<y.r;}
inline void add(int x)
{
if(++cnt[a[x]]==1)
{
if(ans==lsh[a[x]])
{
for(int i=a[x];i<=len;i++)
if(cnt[i])
if(!(lsh[i]+1==lsh[i+1]&&cnt[i+1]))
{
ans=lsh[i]+1;
return;
}
}
}
}
inline void del(int x)
{
if(--cnt[a[x]]==0)
ans=min(ans,lsh[a[x]]);
}
int main()
{
n=read(),m=read();
for(int i=1;i<=n;i++)
a[i]=lsh[i]=read();
sort(lsh+1,lsh+1+n);
len=unique(lsh+1,lsh+1+n)-lsh-1;
for(int i=1;i<=n;i++)
a[i]=lower_bound(lsh+1,lsh+1+len,a[i])-lsh;
fuck(n);
for(int i=1;i<=m;i++)
q[i].l=read(),q[i].r=read(),q[i].id=i;
sort(q+1,q+1+m,cmp);
int l=1,r=0;
for(int i=1;i<=m;i++)
{
int ql=q[i].l,qr=q[i].r;
while(l<ql)del(l++);
while(l>ql)add(--l);
while(r<qr)add(++r);
while(r>qr)del(r--);
res[q[i].id]=ans;
}
for(int i=1;i<=m;i++)
write(res[i]),puts("");
return 0;
}