牛客练习赛68 莫队算法

链接:https://ac.nowcoder.com/acm/contest/7079/A

题目描述

牛牛现在有一个长度为 nnn 的序列 a1,a2,…,ana_1,a_2,\ldots,a_na1,a2,,an。现在牛牛有 qqq 次询问,每次想询问区间 [l,r][l,r][l,r] 的 mex 是什么。

一个序列的 mex 定义为最小未出现的自然数。

输入描述:

第一行两个整数 n,q 表示序列长度和询问次数。
 
接下来一行 n 个非负整数,表示序列 a
 
接下来 q行,每行两个整数 l,r表示询问的区间。

输出描述:

q行,每行表示询问的答案。
示例1

输入

5 2
4 3 0 1 2
2 4
1 5

输出

2
5
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define isdigit(x) ((x) >= '0' && (x) <= '9')
using namespace std;

const int maxn=1e5+7;
int aa[maxn], cnt[maxn];
int n,m, size, bnum, now, ans[maxn];
struct node
{
    int l, r, id, bl;

    bool operator< (const node& b)const
    {
        if(bl != b.bl) return bl < b.bl;
        return r < b.r;
    }
}q[maxn + 5];
int read() {
    int res = 0;
    char c = getchar();
    while(!isdigit(c)) c = getchar();
    while(isdigit(c)) res = (res << 1) + (res << 3) + c - 48, c = getchar();
    return res;
}
void printi(int x) {
    if(x / 10) printi(x / 10);
    putchar(x % 10 + '0');
}
void add(int x){
    ++cnt[aa[x]];
    while(cnt[now]) ++now;
}
void del(int x){
    --cnt[aa[x]];
    if(!cnt[aa[x]]) now=min(now,aa[x]);
}
int main() {
    scanf("%d%d", &n,&m);
    size = sqrt(n);

    for(int i = 1; i <= n; ++i) aa[i] = read(); 
    //m = read();
    for(int i = 1; i <= m; ++i) {
        q[i].l = read(), q[i].r = read();
        q[i].id = i;
        q[i].bl=q[i].l/size;
    }
    sort(q + 1, q + m + 1);
    int l = 1, r = 0;
    for(int i = 1; i <= m; ++i) {
        int ql = q[i].l, qr = q[i].r;
        while(l < ql) del(l),++l;
        while(l > ql) --l,add(l);
        while(r < qr) ++r,add(r);
        while(r > qr) del(r),--r;
        ans[q[i].id] = now;
    }
    for(int i = 1; i <= m; ++i) printi(ans[i]), putchar('\n');
    return 0;
}

 



题解:
莫队优化的暴力,考虑清楚区间增删时对ans的影响就能ac。
下面对l,r移动时的情况进行考虑,
首先如果区间增加时 nex出现了即cnt=1,那么我们就要往后找cnt为0的值更新为nex
区间减少时,nex减少了,那么我们只需要比较当前的nex与删掉的a[x]的大小,取最小的为nex即可。


posted @ 2020-09-09 19:40  杰瑞与汤姆  阅读(210)  评论(0编辑  收藏  举报