bzoj 3585 mex

Written with StackEdit.

题目描述

有一个长度为\(n\)的数组\({a_1,a_2,...,a_n}\)\(m\)次询问,每次询问一个区间内最小没有出现过的自然数。

Input

第一行\(n,m\)
第二行为\(n\)个数。
从第三行开始,每行一个询问\(l,r\)

Output

一行一个数,表示每个询问的答案。

Sample Input

5 5
2 1 0 2 1
3 3
2 3
2 4
1 2
3 5

Sample Output

1
2
3
0
3

HINT

对于\(30\%\)的数据:
\(1<=n,m<=1000.\)

对于\(100\%\)的数据:
\(1<=n,m<=200000,0<=ai<=10^9,1<=l<=r<=n.\)

Solution

  • 多个区间询问,可以离线,并且可以\(O(1)\)转移,考虑使用莫队.
  • 但是\(a_i\leq 10^9\),直接开个\(cnt\)数组记录当前区间内的各种个数似乎不可做?
  • 然而,对于大于\(n\)的元素,我们可以直接无视它.
  • 一方面,它一定不会成为答案,否则需要出现至少\(0\)~\(n\)\(n+1\)个数.
  • 另一方面,它肯定不会对答案做出贡献.否则答案也会大于\(n\),据上,不可能.
  • 所以\(a_i\)的范围只是虚张声势...我们处理时将大于\(n\)的数都视作\(n+1\)即可.
  • 然后就是一个愉快的莫队板子题了.
#include<bits/stdc++.h>
using namespace std;
typedef long long LoveLive;
inline int read()
{
    int out=0,fh=1;
    char jp=getchar();
    while ((jp>'9'||jp<'0')&&jp!='-')
        jp=getchar();
    if (jp=='-')
        {
            fh=-1;
            jp=getchar();
        }
    while (jp>='0'&&jp<='9')
        {
            out=out*10+jp-'0';
            jp=getchar();
        }
    return out*fh;
}
const int MAXN=2e5+10;
int cnt[MAXN];
set<int> s;
int Ans[MAXN];
int n,m;
int a[MAXN];
int belong[MAXN],BlockSize;
int L,R,res;
struct Query{
    int l,r;
    int id;
    bool operator < (const Query &rhs) const 
        {
            if(belong[l]!=belong[rhs.l])
                return belong[l]<belong[rhs.l];
            return belong[r]<belong[rhs.r];
        }
}q[MAXN];
void BuildBlocks()
{
    BlockSize=sqrt(n);
    for(int i=1;i<=n;++i)
        belong[i]=(i/BlockSize)+1;
}
void add(int pos)
{
    ++cnt[a[pos]];
    for(int i=res;i<=n+2;++i)
        if(cnt[i]==0)
            {
                res=i;
                return;
            }
}
void remove(int pos)
{
    --cnt[a[pos]];
    if(cnt[a[pos]]==0)
        res=min(res,a[pos]);
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<=n;++i)
        a[i]=read(),a[i]=a[i]>n?n+1:a[i];
    for(int i=1;i<=m;++i)
        {
            q[i].id=i;
            q[i].l=read();
            q[i].r=read();      
        }
    for(int i=0;i<=n;++i)
        s.insert(i);
    a[0]=n+2;
    BuildBlocks();
    sort(q+1,q+1+m);
    L=0,R=0;
    for(int i=1;i<=m;++i)
        {
            int l=q[i].l,r=q[i].r;
            while(L<l)
                remove(L),++L;
            while(L>l)
                --L,add(L);
            while(R<r)
                ++R,add(R);
            while(R>r)
                remove(R),--R;
            Ans[q[i].id]=res;
        }
    for(int i=1;i<=m;++i)
        printf("%d\n",Ans[i]);
    return 0;
}

转载于:https://www.cnblogs.com/jklover/p/10105545.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值