题面描述
题解
想必同学们都是刷莫队的时候不小心刷到这题的
主要考虑怎么修改和查询
类似值域线段树的思想,将值域
O(0,K)
O
(
0
,
K
)
进行分块,查询时先遍历所有的块,如果某个块非空那么查询该块,当查询到一个数字为空时,即可break输出答案,复杂度为O(
k√
k
+
k√
k
),修改时要注意判断块的信息
1.增加一个数时,如果该数为空,则要将该数所在块的元素个数和该数的个数都+1,否则只将该数的个数+1
2.删除一个数时,如果该数删除后为空,则要将该数所在块的元素个数和该数的个数都-1,否则只将该数的个数-1
细节:
考虑到最坏情况下的区间是
(0,1,2,3,......N−1)
(
0
,
1
,
2
,
3
,
.
.
.
.
.
.
N
−
1
)
因此最大答案也是N,所以A_i>=1e9是用来搞笑的,将所有大于等于N+1的数修改为N+1就可以了
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int _=200005;
const int K=448;
inline int read()
{
char ch='!';int z=1,num=0;
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')z=-1,ch=getchar();
while(ch<='9'&&ch>='0')num=(num<<3)+(num<<1)+ch-'0',ch=getchar();
return z*num;
}
int n,m,kuai[_],a[_];
struct hand{int l,r,id;}ask[_];
bool cmp(hand A,hand B)
{
if(kuai[A.l]==kuai[B.l])return A.r<B.r;
return kuai[A.l]<kuai[B.l];
}
int num[500],cnt[_],ans[_];
void add(int x)
{
if(!cnt[x])num[x/K]++;
cnt[x]++;
}
void del(int x)
{
if(cnt[x]==1)num[x/K]--;
cnt[x]--;
}
void Query(int x)
{
for(int i=1;i<=K;i++)
{
if(num[i-1]!=K)
{
for(int j=(i-1)*K;j<i*K;j++)
{
if(!cnt[j])
{
ans[ask[x].id]=j;return;
}
}
}
}
}
int main()
{
n=read();m=read();
int len=sqrt(n);
for(int i=1;i<=n;i++)
{
a[i]=min(read(),n+1);
kuai[i]=(i-1)/len+1;
}
for(int i=1;i<=m;i++)
ask[i].l=read(),ask[i].r=read(),ask[i].id=i;
sort(ask+1,ask+1+m,cmp);
int l=ask[1].l,r=ask[1].r;
for(int i=l;i<=r;i++)
add(a[i]);
Query(1);
for(int i=2;i<=m;i++)
{
while(l<ask[i].l)del(a[l++]);
while(l>ask[i].l)add(a[--l]);
while(r<ask[i].r)add(a[++r]);
while(r>ask[i].r)del(a[r--]);
Query(i);
}
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
return 0;
}
Thanks for your attention.