[BZOJ3339]Rmq Problem 题解
简单的数据结构题,他没有强制在线,我们可以先想离线,这题的离线操作刚好会比在线轻松一点。
这题离线的话有点像二维数点? 很明显,先求出对于每一个 \(i\),\(i\sim r\) 的 mex
是多少,然后对于再加入一个 \(r+1\) 我们的贡献是多少,但是会发现这个东西好像不能很好地用数据结构来实现正确的时间复杂度(⊙o⊙)…这怎么办?想想,既然本来他让我们求的就是少的那个,那你每次加一个不是给自己徒增难度?不如直接每次减一个嘛,这样从 \(r\rightarrow r-1,pre_{r}+1\sim r-1\) 会失去 \(a_r\) 的影响,这样mex
就相当于跟 \(a_r\) 去最小值。
这个不就是简单的线段树了吗,秒了。
#include<bits/stdc++.h>
#define pii pair<int,int>
using namespace std;
const int N=2E5+5;
int n,m;
int a[N],pre[N],b[N];
int t[N],mi;
int lea[N],ans[N];
struct SEG{
#define ls p<<1
#define rs p<<1|1
int c[N<<2],tag[N<<2];
void pushdown(int p){
if(tag[p]==1e9)return ;
tag[ls]=min(tag[p],tag[ls]);
c[ls]=min(c[ls],tag[p]);
tag[rs]=min(tag[p],tag[rs]);
c[rs]=min(c[rs],tag[p]);
tag[p]=1e9;
}
void build(int p,int l,int r){
tag[p]=1e9;
if(l==r){
c[p]=b[l];
lea[l]=p;
return ;
}
int mid=l+r>>1;
build(ls,l,mid),build(rs,mid+1,r);
}
void change(int p,int l,int r,int L,int R,int w){
if(L<=l&&r<=R){
tag[p]=min(tag[p],w);
c[p]=min(w,c[p]);
return ;
}
pushdown(p);
int mid=l+r>>1;
if(L<=mid)change(ls,l,mid,L,R,w);
if(R>mid)change(rs,mid+1,r,L,R,w);
}
int query(int p,int l,int r,int x){
if(l==r)return c[p];
pushdown(p);
int mid=l+r>>1;
if(mid>=x)return query(ls,l,mid,x);
else return query(rs,mid+1,r,x);
}
}seg;
vector<pii>e[N];
signed main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",a+i);
for(int i=1;i<=m;i++){
int l,r;
scanf("%d%d",&l,&r );
e[r].push_back({l ,i});
}
for(int i=1;i<=n;i++){
pre[i]=t[a[i]];
t[a[i]]=i;
}
memset(t,0,sizeof t);
mi=0;
for(int i=n;i>=1;i--){
t[a[i]]=1;
while(t[mi])mi++;
b[i]=mi;
}
seg.build(1,1,n);
for(int i=n;i;i--){
for(pii tmp:e[i]){
int x=tmp.first,y=tmp.second;
ans[y]=seg.query(1,1,n,x);
}
if(i!=1)seg.change(1,1,n,pre[i]+1,i-1,a[i]);
}
for(int i=1;i<=m;i++){
cout<<ans[i]<<endl;
}
//i~r->i~r-1
//1~pre[r] 不变
//pre[r]+1~r-1 对 a[r] 取min
return 0;
}