Loading

P8496 [NOI2022] 众数

补题狗不发表评论,但是摩尔投票是不是有点那啥

小心 deque

摩尔投票

对于一个数列,如果其众数出现次数严格大于数列长度的一半,就可以用摩尔投票的方式 \(O(n)\) 求出众数。

当众数出现次数小于一半时,求出众数之后还需要检验。

模板:P2397 yyy loves Maths VI (mode)

摩尔投票具有可加性,因此可以用线段树维护区间摩尔投票。例题:P3765 总统选举

思路

线段树合并 + 摩尔投票。

首先注意到题目给出众数出现次数严格大于一半的条件,考虑用摩尔投票求众数。

正常想法是对每个序列维护一棵普通线段树,但是这样的话插入需要记录长度之类的很麻烦,而且检验众数的时候不能快速求一个值在某个数列中的出现次数,所以考虑换成值域线段树。

对于 1 2 操作,需要动态维护每个序列两端的数,也就是需要动态维护每个数列。注意到不同的数列之间只有合并操作,考虑启发式合并维护 4 操作。

因为要访问两端所以用 deque,结果发现 1e6 个压缩 deque 遇到 CCF 评测机变答辩糕,直接 MLE 送走一车人。遂怒写 1e6 个链表模拟 deque.

这样对于 1 2 操作只需要简单维护一下,4 用线段树合并 + 启发式合并链表可以做,接下来考虑 3 操作。

显然拼接完给的区间之后,大区间的众数只有可能在给出区间的众数中取。反之每个区间中都是非众数的数出现次数均小于一半,加起来显然也不可能是大区间的众数。

于是我们只需要把给出的每个区间中的众数拉出来,再做一次摩尔投票就行了。检验直接用值域线段树算出现次数。

时间复杂度 \(O(n \log n)\).

代码

#include <cstdio>
#include <utility>
using namespace std;

#define fi first
#define se second
typedef long long ll;

const int maxn = 1e6 + 5;
const int sgt_sz = maxn * 30;

int n, q, id;
int se[maxn], rt[maxn];
int sz[maxn], head[maxn], pre[maxn], lst[maxn], val[maxn];

namespace SGT
{
    int nd;
    int ls[sgt_sz], rs[sgt_sz], val[sgt_sz], cnt[sgt_sz];
    
    void push_up(int rt)
    {
        if (cnt[ls[rt]] > cnt[rs[rt]]) val[rt] = val[ls[rt]], cnt[rt] = cnt[ls[rt]];
        else val[rt] = val[rs[rt]], cnt[rt] = cnt[rs[rt]];
    }
    
    void update(int &rt, int l, int r, int p, int w)
    {
        if (!rt) rt = ++nd;
        if (l == r) return val[rt] = l, cnt[rt] += w, void();
        int mid = (l + r) >> 1;
        if (p <= mid) update(ls[rt], l, mid, p, w);
        else update(rs[rt], mid + 1, r, p, w);
        push_up(rt);
    }
    
    int query(int rt, int l, int r, int p)
    {
        if (!rt) return 0;
        if (l == r) return cnt[rt];
        int mid = (l + r) >> 1;
        if (p <= mid) return query(ls[rt], l, mid, p);
        return query(rs[rt], mid + 1, r, p);
    }
    
    void merge(int &rt, int a, int b, int l, int r)
    {
        if ((!a) || (!b)) return rt = a | b, void();
        if (!rt) rt = ++nd;
        if (l == r) return val[rt] = l, cnt[rt] = cnt[a] + cnt[b], void();
        int mid = (l + r) >> 1;
        merge(ls[rt], ls[a], ls[b], l, mid);
        merge(rs[rt], rs[a], rs[b], mid + 1, r);
        push_up(rt);
    }
}

void clear(int x) { head[x] = sz[x] = lst[x] = 0; }

void psh_back(int x, int y)
{
    val[++id] = y, sz[x]++;
    if (!head[x]) head[x] = id;
    pre[id] = lst[x], lst[x] = id;
}

void pp_back(int x)
{
    lst[x] = pre[lst[x]], sz[x]--;
    if (!sz[x]) head[x] = 0;
}

int main()
{
    scanf("%d%d", &n, &q);
    int vl = 0, vr = n + q + 1;
    for (int i = 1, l; i <= n; i++)
    {
        scanf("%d", &l);
        for (int j = 1, v; j <= l; j++)
        {
            scanf("%d", &v), v++;
            psh_back(i, v);
            SGT::update(rt[i], vl, vr, v, 1);
        }
    }
    while (q--)
    {
        int opt;
        scanf("%d", &opt);
        if (opt == 1)
        {
            int x, y;
            scanf("%d%d", &x, &y), y++;
            psh_back(x, y), SGT::update(rt[x], vl, vr, y, 1);
        }
        else if (opt == 2)
        {
            int x;
            scanf("%d", &x);
            SGT::update(rt[x], vl, vr, val[lst[x]], -1), pp_back(x);
        }
        else if (opt == 3)
        {
            int m;
            scanf("%d", &m);
            for (int i = 1; i <= m; i++) scanf("%d", &se[i]);
            pair<int, ll> nw(0, 0); ll cur = 0;
            for (int i = 1; i <= m; i++)
            {
                cur += sz[se[i]];
                if (SGT::cnt[rt[se[i]]] <= sz[se[i]] / 2) continue;
                ll res = SGT::cnt[rt[se[i]]] * 2ll - sz[se[i]];
                if (SGT::val[rt[se[i]]] == nw.fi) nw.se += res;
                else if (nw.se == res) nw = {0, 0};
                else if (nw.se < res) nw = {SGT::val[rt[se[i]]], res - nw.se};
                else nw.se -= res;
            }
            if (!nw.fi) { puts("-1"); continue; }
            ll chk = 0;
            for (int i = 1; i <= m; i++) chk += SGT::query(rt[se[i]], vl, vr, nw.fi);
            if (chk > cur / 2) printf("%d\n", nw.fi - 1);
            else puts("-1");
        }
        else
        {
            int a, b, c;
            scanf("%d%d%d", &a, &b, &c);
            SGT::merge(rt[c], rt[a], rt[b], vl, vr);
            lst[c] = (lst[b] ? lst[b] : lst[a]), head[c] = (head[a] ? head[a] : head[b]);
            if (head[b]) pre[head[b]] = lst[a];
            sz[c] = sz[a] + sz[b];
            clear(a), clear(b);
        }
    }
    return 0;
}
posted @ 2023-05-13 15:35  kymru  阅读(62)  评论(0编辑  收藏  举报