3 条题解

  • 3
    @ 2024-7-4 16:39:19

    MXOJ Next Problem 3 Solution

    题目

    here

    思路

    询问离线和双树状数组解法还是太复杂了。

    首先,题面的意思就是完全不用考虑糖果的顺序,只考虑每一个糖果种类的数量。

    愤怒值的本质,就是分给一个小朋友的糖果数量减去种数。

    但是,这种方法前人已经提过了,考虑换一个角度。

    kk 个小朋友分糖果,本质上相当于最多有 kk 个同种糖果不会对答案产生贡献。

    也就是说,令 m=n+qm=n+qcic_i 为目前第 ii 种糖果的个数,则所有糖果中最多有 i=1mmin(k,ci)\sum_{i=1}^m \min(k,c_i) 个不对答案做贡献。

    将糖果总数减去上述表达式的值即为答案。

    解题方法

    使用树状数组对 i=1mmin(k,ci)\sum_{i=1}^m \min(k,c_i) 进行维护。

    复杂度

    时间复杂度

    m=n+qm=n+q

    每次增加、减少、查询时都需要对树状数组进行操作,故单次操作时间复杂度为 O(logm)O(\log m)

    总时间复杂度 O(qlogm)O(q\log m)

    空间复杂度

    m=n+qm=n+q

    最多可能有 O(m)O(m) 个糖果,故树状数组的大小为 O(m)O(m)

    总空间复杂度 O(m)O(m)

    Code

    #include <iostream>
    using namespace std;
    const int N = 2e6 + 10;
    int n, q, op, x;
    int tr[N], buc[N];
    inline void update(int x, int y)
    {
        for (; x < N; x += (x & -x))
            tr[x] += y;
    }
    inline int query(int x)
    {
        int res = 0;
        for (; x; x -= (x & -x))
            res += tr[x];
        return res;
    }
    int main()
    {
        scanf("%d%d", &n, &q);
        for (int i = 1; i <= n; i++)
        {
            scanf("%d", &x);
            buc[x]++;
            update(buc[x], 1);
        }
        for (int i = 1; i <= q; i++)
        {
            scanf("%d%d", &op, &x);
            if (op == 3)
            {
                printf("%d\n", n - query(x));
                continue;
            }
            if (op == 1)
            {
                buc[x]++, n++;
                update(buc[x], 1);
                continue;
            }
            update(buc[x], -1);
            buc[x]--, n--;
        }
    }
    

    信息

    ID
    3
    时间
    1000ms
    内存
    512MiB
    难度
    4
    标签
    递交数
    465
    已通过
    95
    上传者