题目链接
题目解法
观察样例可知自然数包括0(废话)
算法1
这道题可以用回滚莫队做
考虑时间复杂度
可以想到答案一定不超过区间的长度
那么块内加数:暴力修改答案每次需要
跨块加数:因为有个块,每个块中右端点从小到大,答案必定只加不减,所以每一块是,时间复杂度是
所以回滚莫队的时间复杂度是
#include <bits/stdc++.h>
using namespace std;
const int N(200100),M(200100),S(200100);
struct Query{
int id,l,r;
}query[M];
int n,m,a[N],pos[N],cnt[S],ans[M];
inline int read(){
int FF=0,RR=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar())
if(ch=='-')
RR=-1;
for(;isdigit(ch);ch=getchar())
FF=(FF<<1)+(FF<<3)+ch-48;
return FF*RR;
}
bool cmp(const Query &x,const Query &y){
return pos[x.l]^pos[y.l]?pos[x.l]<pos[y.l]:x.r<y.r;
}
void add(int x,int &res){
cnt[x]++;
while(cnt[res]&&cnt[res+1]&&cnt[res+2])
res+=3;
while(cnt[res])
res++;
}
int main(){
n=read(),m=read();
int B=sqrt(n);
for(int i=1;i<=n;i++)
a[i]=read(),pos[i]=(i-1)/B+1;
for(int i=1,l,r;i<=m;i++)
l=read(),r=read(),query[i]={i,l,r};
sort(query+1,query+m+1,cmp);
for(int k=1;k<=m;){
int b=pos[query[k].l];
int _r=b*B,i=_r+1,j=_r;
int res=0;
for(;pos[query[k].l]==b;k++){
int id=query[k].id,l=query[k].l,r=query[k].r;
if(pos[query[k].r]==b){
for(int i=l;i<=r;i++)
add(a[i],res);
ans[id]=res;
res=0;
for(int i=l;i<=r;i++)
cnt[a[i]]=0;
}
else{
while(j<r)
add(a[++j],res);
int bres=res;
while(i>l)
add(a[--i],res);
ans[id]=res;
res=bres;
while(i<=_r)
cnt[a[i++]]--;
}
}
memset(cnt,0,sizeof(cnt));
}
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
return 0;
}
这道题因为比较卡常,所以莫队只能拿到只能90分
算法2
看到没有出现的几个字会联想到SDOI2009 HH的项链
于是我们考虑用可持久化线段树来做
但是一个数是否出现过不好用线段树节点之间的加减来维护
需要考虑维护其他信息达到此效果
可持久化线段树常见的套路是记录权值相同的数出现的最大位置pos
我们这道题也可以用这个套路
如果区间[l,r]中有权值的pos<l,那么这个权值一定没有在[l,r]中出现过
所以这启发我们在可持久化线段树上二分完成这个问题,离线下来可以不用可持久化
时间复杂度
#include <bits/stdc++.h>
using namespace std;
const int N(200100);
struct SGT{
int lc,rc,mini;
}seg[N*4+N*20];
int n,m;
int root[N],idx;
inline int read(){
int FF=0,RR=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar())
if(ch=='-')
RR=-1;
for(;isdigit(ch);ch=getchar())
FF=(FF<<1)+(FF<<3)+ch-48;
return FF*RR;
}
int build(int l,int r){
int p=++idx;
if(l==r)
return p;
int mid=(l+r)>>1;
seg[p].lc=build(l,mid),seg[p].rc=build(mid+1,r);
return p;
}
int insert(int p,int l,int r,int pos,int k){
int q=++idx;
seg[q]=seg[p];
if(l==r){
seg[q].mini=k;
return q;
}
int mid=(l+r)>>1;
if(mid>=pos)
seg[q].lc=insert(seg[p].lc,l,mid,pos,k);
else
seg[q].rc=insert(seg[q].rc,mid+1,r,pos,k);
seg[q].mini=min(seg[seg[q].lc].mini,seg[seg[q].rc].mini);
return q;
}
int query(int p,int l,int r,int k){
if(l==r)
return l;
int mid=(l+r)>>1;
if(seg[seg[p].lc].mini<k)
return query(seg[p].lc,l,mid,k);
else
return query(seg[p].rc,mid+1,r,k);
}
int main(){
n=read(),m=read();
root[0]=build(0,N-1);
for(int i=1,x;i<=n;i++)
x=read(),root[i]=insert(root[i-1],0,N-1,x,i);
for(int i=1,l,r;i<=m;i++){
l=read(),r=read();
printf("%d\n",query(root[r],0,N-1,l));
}
return 0;
}