zoukankan      html  css  js  c++  java
  • 牛客:牛牛的mex(莫队+分块)

    题意:

    链接:https://ac.nowcoder.com/acm/problem/210690
    来源:牛客网

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

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

    题解:

    直接暴力就能过,但是如果去掉题目中的互不相同性质,暴力就不行,

    这里给出一个支持扩展版本的做法,就是莫队加分块。

    莫队优化询问,然后从1~num遍历分好的块,遇到有块空的就在这个块里面找答案。

    总体时间复杂度是O(n√n)。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1e5+100;
    int a[maxn];
    int n,m;
    struct query {
        int l,r,id;
    }q[maxn];
    int ans[maxn];
    int belong[maxn];
    int size;
    int bnum;
    int cnt[maxn];
    int st[maxn];//每个块的起点 
    int ed[maxn];//每个块的终点 
    int pos[maxn];//每个点属于哪个块 
    int mk[maxn];//标记每个块的现有元素数量 
    int num[maxn];//每个块的大小 
    int now;
    int cmp (query a,query b) {
        return (belong[a.l]^belong[b.l])?belong[a.l]<belong[b.l]:((belong[a.l]&1)?a.r<b.r:a.r>b.r);
    }
    void add (int p) {
        cnt[a[p]]++;
        if (a[p]==0) return;
        if (cnt[a[p]]==1) mk[pos[a[p]]]++;
    }
    void del (int p) {
        cnt[a[p]]--;
        if (a[p]==0) return;
        if (cnt[a[p]]==0) mk[pos[a[p]]]--;
    }
    int main () {
        scanf("%d%d",&n,&m);
        int tt=sqrt(n);
        for (int i=1;i<=tt;i++) {
            st[i]=(i-1)*tt+1;
            ed[i]=i*tt;
            num[i]=tt;
        }
        if (ed[tt]<n) {
            tt++;
            st[tt]=ed[tt-1]+1;
            ed[tt]=n;
            num[tt]=n-(tt-1)*(tt-1);
        }
        for (int i=1;i<=tt;i++) {
            for (int j=st[i];j<=ed[i];j++) {
                pos[j]=i;
            }
        }
        size=sqrt(m);
        bnum=m/size+(m%size>0);
        for (int i=1;i<=bnum;i++) {
            for (int j=(i-1)*size+1;j<=i*size;j++)
                belong[j]=i;
        }
        
        for (int i=1;i<=n;i++) scanf("%d",&a[i]);
        for (int i=1;i<=m;i++) {
            int l,r;
            scanf("%d%d",&q[i].l,&q[i].r);
            q[i].id=i;
        }
        sort(q+1,q+m+1,cmp);
        int l=1,r=0;
        for (int i=1;i<=m;i++) {
            int ql=q[i].l;
            int qr=q[i].r;
            while (l<ql) del(l++);
            while (l>ql) add(--l);
            while (r<qr) add(++r);
            while (r>qr) del(r--);
            if (!cnt[0])
                now=0;
            else {
                now=0;
                for (int i=1;i<=tt;i++) {
                    if (mk[i]<num[i]) {
                        for (int j=st[i];j<=ed[i];j++) {
                            if (!cnt[j]) {
                                now=j;
                                break;
                            }
                        }
                        break;
                    }
                }
                  
            }
            ans[q[i].id]=now;
        }
        for (int i=1;i<=m;i++) printf("%d
    ",ans[i]);
    }
  • 相关阅读:
    扩展欧几里得算法
    单源最短路径—Dijkstra算法
    欧拉定理,费马小定理
    欧拉函数
    Trie 字典树
    平衡树——Treap,Splay
    NOI2009 开关
    银河英雄传说
    线段树与延迟标记
    c++常见变量的极值
  • 原文地址:https://www.cnblogs.com/zhanglichen/p/13580173.html
Copyright © 2011-2022 走看看