Luogu | P4137 [高2021级考试 19.5.4] RMQ Problem / mex | 数据结构 / 莫队 | 离线 / 线段树
Description
给定一个长度为 $N$ 的数列 $A$ ,数列的第 $i$ 个元素是 $A_i$(从$1$开始编号)。你需要回答 $q$ 个询问,每个询问的参数是一个二元组 $(l,r)$,表示查询 $mex({A[l],A[l+1],…,A[r]})$ 的值。
所谓 $mex$ ,就是 $SG$ 定理中那个函数,也就是
Mininum Exclusive
的缩写。对一个一个非负整数组成的集合 $S$ ,$mex(S)$ 的值等于最小的不属于集合 $S$ 的非负整数。
Input Format
第一行包含 $2$ 个空格分开的整数 $N,Q$ ,代表序列 $A$ 的长度和询问个数。
第二行包含 $N$ 个空格隔开的非负整数,第 $i$ 个整数表示 $A_i$ 的值。
接下来的 $Q$ 行 ,每行包含 $2$ 个空格分开的正整数 $l$ 和 $r$ ,表示一个查询参数。
Output Format
包含 $Q$ 行,每行一个正整数,表示对应询问的答案。
Input Sample
Sample 01
7 5
0 2 1 0 1 3 2
1 3
2 3
1 4
3 6
2 7
Output Sample
Sample 01
3
0
3
2
4
Data Range
$ 1 \le N,Q \le 2 \times 10^5 $,$ 0 \le A_i \le 10^9 $。
Solution
与前一道题类似,这里要用到离线 + 离散化。先将问题按照右端点排序。
使用 $l_i$ 维护值为 $i$ 的元素当前在最右边出现的位置。用线段树维护 $l_i$ (合并时取最小值),每次询问$(ql,qr)$则在树上查询一个满足 $i$ 最小并且 $l_i < ql$ 的元素。可以由集合线段树的 kth
操作修改而来。随着序列向右扫描的过程回答询问即可。
需要注意的一点是,在离散化的过程中,对于任意一个初始序列中的元素 $x$ ,需要将 $x-1$ 和 $x+1$ 都加入集合中(这样才能回答出“没有出现过的数字”)。当然,如果 $x=0$ 就不用添加 $x-1$ 了。
当然,也可以莫队 / 可持久化(不用离线)。如果你闲得慌,还可以动态开点(不用离散化)。
Code
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
#define swap(x,y) {int t=x; x=y,y=t;}
#define wipe(x,y) memset(x,y,sizeof(x))
#define dbgIn(x) freopen(x".in","r+",stdin)
#define rep(x,y,z) for(int x=y,I=z;x<=I;++x)
#define dbgOut(x) freopen(x".out","w+",stdout)
#define file(x) freopen(x".in","r+",stdin); freopen(x".out","w+",stdout)
#define LC lch[now]
#define RC rch[now]
#define mid ((L+R)>>1)
using std::sort;
using std::lower_bound;
inline void Read(int &x){
x=0; char ch=0; while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar(); return;
}
struct data{
int ql,qr,id;
friend bool operator < (data a,data b){
if(a.qr==b.qr) return a.ql<b.ql;
return a.qr<b.qr;
}
}q[200005];
int root,node,N,Q,po,lim;
int src[200005],srt[600005],ans[200005],minv[1200005],lch[1200005],rch[1200005];
void upload(int now){
minv[now]=min(minv[LC],minv[RC]); return;
}
void build(int &now,int L,int R){
now=++node; if(L==R) return;
build(LC,L,mid); build(RC,mid+1,R); return;
}
void modify(int now,int L,int R,int pos,int val){
if(L==R){ minv[now]=val; return; }
if(pos<=mid) modify(LC,L,mid,pos,val);
else modify(RC,mid+1,R,pos,val); upload(now);
}
int query(int now,int L,int R,int K){
if(L==R) return L;
if(minv[LC]<K) return query(LC,L,mid,K);
else return query(RC,mid+1,R,K);
}
int main(){
Read(N); Read(Q);
rep(i,1,N) { Read(src[i]); srt[i]=src[i]; }
sort(srt+1,srt+1+N); po=N; srt[0]=-2;
rep(i,1,N){ if(srt[i]==srt[i-1]) continue;
if(srt[i]!=0)srt[++po]=srt[i]-1; srt[++po]=srt[i]+1;
}sort(srt+1,srt+1+po); srt[++po]=0x7f7f7f7f;
rep(i,1,po) if(srt[i]==srt[i-1]) srt[i-1]=0x7f7f7f7f;
sort(srt+1,srt+1+po);
build(root,1,lim=lower_bound(srt+1,srt+1+po,0x7f7f7f7f)-srt);
rep(i,1,N) src[i]=lower_bound(srt+1,srt+1+lim,src[i])-srt;
rep(i,1,Q) { Read(q[i].ql); Read(q[i].qr); q[i].id=i; }
sort(q+1,q+1+Q); int j=1; rep(i,1,N){
modify(root,1,lim,src[i],i);
while(q[j].qr==i){
ans[q[j].id]=srt[query(root,1,lim,q[j].ql)]; j++;
}
}rep(i,1,Q) printf("%d\n",ans[i]);
return 0;
}