MXOJ Next Problem 3 Solution

题目

here

思路

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

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

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

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

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

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

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

解题方法

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

复杂度

时间复杂度

\(m=n+q\)

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

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

空间复杂度

\(m=n+q\)

最多可能有 \(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--;
    }
}
posted @ 2024-07-04 16:41  丝羽绫华  阅读(8)  评论(0编辑  收藏  举报