题解:Rmq Problem / mex

题面描述

传送门

题解

想必同学们都是刷莫队的时候不小心刷到这题的
主要考虑怎么修改和查询
类似值域线段树的思想,将值域 O(0,K) O ( 0 , K ) 进行分块,查询时先遍历所有的块,如果某个块非空那么查询该块,当查询到一个数字为空时,即可break输出答案,复杂度为O( k k + k k ),修改时要注意判断块的信息
1.增加一个数时,如果该数为空,则要将该数所在块的元素个数和该数的个数都+1,否则只将该数的个数+1
2.删除一个数时,如果该数删除后为空,则要将该数所在块的元素个数和该数的个数都-1,否则只将该数的个数-1
细节:
考虑到最坏情况下的区间是 (0,1,2,3,......N1) ( 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.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值