题目:BZOJ3585/lugou4137.
题目大意:给定一个长度为
的数列以及
个询问,每个询问查询区间
中最小的没有出现的自然数.
.
接下来我们称 内最小的没有出现的自然数为 .
首先容易发现 ,因为 中的数最坏情况下只会把 填满,所以本题并不需要离散化.
现在考虑一个莫队的做法,用一个计数数组记录当前区间 中每个权值的出现次数和 .然后删除一个权值的时候,若这个点空了,则尝试更新当前答案;插入一个权值的时候,若刚好是当前答案呗搞掉了,就大力往后查找第一个空的位置.
这个做法怎么看都是假的,然而却有人说这样做是可以证明每次插入操作的单调性什么的来保证均摊 …
现在我们来hack一下这个做法,容易想到一个简单的构造数据的方法:考虑生成一个序列 ,然后每次询问的端点按照 循环,右端点直接递增生成,譬如 即可.
然后我们就开心愉快地把这道题的朴素莫队做法卡掉了,这样的数据让朴素莫队跑理论上可以把它卡到 …
当然这道题的莫队可以通过树状数组+倍增做到 或者用分块优化或直接回滚莫队做到 .
那么现在我们不考虑莫队,直接用主席树是否可做呢?
考虑直接记录每一个前缀的权值,然后发现这个转化为权值后的信息并不满足区间减法性质…
换一个思路,用主席树记录每个前缀的每一个权值的最后出现的位置(即下标),然后每次查询 就可以通过第 棵主席树上二分做.具体就是二分权值 ,然后看 中记录的下标的最小值是否小于 ,若小于 说明答案在 上,否则说明答案在 上.
时空复杂度 .
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=200000,C=30;
int n,m,a[N+9];
struct tree{
int min,s[2];
}tr[N*C+9];
int cn,rot[N+9];
void Build(int &k){tr[k=cn=0]=tree();}
void Pushup(int k){tr[k].min=min(tr[tr[k].s[0]].min,tr[tr[k].s[1]].min);}
void Change_val(int p,int v,int l,int r,int hk,int &k){
tr[k=++cn]=tr[hk];
if (l==r){tr[k].min=v;return;}
int mid=l+r>>1;
p<=mid?Change_val(p,v,l,mid,tr[hk].s[0],tr[k].s[0]):Change_val(p,v,mid+1,r,tr[hk].s[1],tr[k].s[1]);
Pushup(k);
}
int Query_mex(int p,int l,int r,int k){
if (l==r) return l;
int mid=l+r>>1;
return p>=tr[tr[k].s[0]].min?Query_mex(p,l,mid,tr[k].s[0]):Query_mex(p,mid+1,r,tr[k].s[1]);
}
Abigail into(){
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i){
scanf("%d",&a[i]);
}
}
Abigail work(){
Build(rot[0]);
for (int i=1;i<=n;++i)
if (a[i]>n) rot[i]=rot[i-1];
else Change_val(a[i],i,0,n,rot[i-1],rot[i]);
}
Abigail getans(){
int l,r;
for (int i=1;i<=m;++i){
scanf("%d%d",&l,&r);
printf("%d\n",Query_mex(l-1,0,n,rot[r]));
}
}
int main(){
into();
work();
getans();
return 0;
}