1 条题解

  • 1
    @ 2023-2-3 10:41:58

    P4137 Rmq Problem / mex

    #include <bits/stdc++.h>
    using namespace std;
    const int MAXN = 200000 + 5;
    int n, m;
    int mm;         // 块的大小
    int qid[MAXN];  // qid[i]: i/m,第i个数所属的块,块 id 从 0 开始
    int qcnt[MAXN]; // qcnt[i]: 第i个块里面出现了多少个不同的数
    int qall[MAXN]; // qall[i]: 第i个块里面一共有多少个数
    int a[MAXN], maxAi;
    int cnt[MAXN];
    struct Q
    {
        int l, r, id, ans;
    } q[MAXN];
    bool cmp(Q a, Q b)
    {
        if (qid[a.l] != qid[b.l])
            return qid[a.l] < qid[b.l];
        return a.r < b.r;
    }
    bool cmpp(Q a, Q b)
    {
        return a.id < b.id;
    }
    int nowAns;
    // 添加了 a[x] 后,更新答案
    void add(int x)
    {
        cnt[a[x]]++;
        // 如果是新出现的数
        if (cnt[a[x]] == 1)
        {
            qcnt[qid[a[x]]]++;
            // 如果原来的答案现在出现了,更新答案
            if (a[x] == nowAns)
            {
                // 先找到是哪个块,再来定位是哪个数
                // 从这个块开始往后找
                int nowq = qid[a[x]];
                // 如果当前块的数全出现过了,那就肯定不在这个块里,去下个块找
                while (qcnt[nowq] == qall[nowq])
                    nowq++;
                // 先认为答案是第 nowq 个块的第一个数 (0,mm-1) (mm,2mm-1) (2mm,3mm-1)
                nowAns = nowq * mm;
                // 如果不是这个就暴力跳到下一个
                while (cnt[nowAns] != 0)
                    nowAns++;
            }
        }
    }
    void del(int x)
    {
        cnt[a[x]]--;
        if (cnt[a[x]] == 0)
        {
            nowAns = min(a[x], nowAns);
            qcnt[qid[a[x]]]--;
        }
    }
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);
        cin >> n >> m;
        //1~n 每个位置分块,求出 qid[i]: 第 i 个位置所属的块
        mm = sqrt(n);
        maxAi = 0;
        for (int i = 1; i <= n; i++)
        {
            cin >> a[i];
            maxAi = max(maxAi, a[i]);
            qid[i] = i / mm;
        }
        for (int i = 1; i <= m; i++)
        {
            cin >> q[i].l >> q[i].r;
            q[i].id = i;
        }
        // 对询问排序
        // 左端点所属的块相同时按照右端点排序,不同时按照左端点所属的块排序
        sort(q + 1, q + m + 1, cmp);
        // 排完序后复用前面的qid数组,完成对cnt数组的分块
        // cnt[i]: 数字i的出现次数
        //  qid[i]: i/m,第i个数所属的块,块 id 从 0 开始
        //  qcnt[i]: 第i个块里面出现了多少个不同的数
        //  qall[i]: 第i个块里面一共有多少个数
        mm = sqrt(maxAi + 1);
        // 0:(0,mm-1) 1:(mm,2mm-1) 2:(2mm,3mm-1)
        for (int i = 0; i <= maxAi + 1; i++)
        {
            cnt[i] = 0;
            qid[i] = i / mm;
            qall[qid[i]]++;
            qcnt[qid[i]] = 0;
        }
        // 把排完序的询问一条条处理答案
        // 初始设置为第一个区间
        int L = q[1].l;
        int R = q[1].r;
        nowAns = 0;
        for (int i = L; i <= R; i++)
            add(i);
        q[1].ans = nowAns;
        // 第二个区间开始由上一个区间转移得到
        for (int i = 2; i <= m; i++)
        {
            while (q[i].l < L)
                add(--L);
            while (q[i].l > L)
                del(L++);
            while (q[i].r < R)
                del(R--);
            while (q[i].r > R)
                add(++R);
            q[i].ans = nowAns;
        }
        // 重新按照问题 id 排序,输出每个问题的答案
        sort(q + 1, q + m + 1, cmpp);
        for (int i = 1; i <= m; i++)
            cout << q[i].ans << "\n";
        return 0;
    }
    
    
    • 1

    信息

    ID
    1217
    时间
    1000ms
    内存
    256MiB
    难度
    5
    标签
    递交数
    18
    已通过
    16
    上传者