1 条题解

  • 0
    @ 2026-5-13 23:51:06

    为本题的启发式合并+分治做法提供更详细的讲解和更简洁的代码(另:欢迎 hack)

    对于 1、2 操作,发现需要插入、删除最后一个节点。又看到 3、4 操作的拼接序列,果断选择链表,为了实现简单使用了 list。同时还需要维护每个序列的众数(不是绝对众数)及序列中每个数的出现次数。这两个分别可以用 setmap 来维护。

    对于操作 3,可以分治求解。将一个序列任意地分成两份,整个序列的绝对众数一定是其中某份的绝对众数。对于分治出来的两边的答案,分别检查即可。时间复杂度 O(mlogm)O(m \log m)

    对于操作 4,可以直接启发式合并两个序列的 setmap,时间复杂度摊下来应该是 O(log2n)O(\log^2 n) 的,但是跑得飞快,而且还不用特判 x1x_1x2x_2 为空的情况。对于合并出来在较大序列的信息,可以直接 swapx3x_3list 的拼接可以用 list::splice O(1)O(1) 实现。

    时间复杂度 O(qlog2n)O(q \log^2 n)

    然后这道题就做完了,代码也很简单,完全不压行也只有 9393 行。个人认为这种做法的思路比线段树合并简单很多。

    代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    #define MAXN 1000001
    
    int n, q;
    int b[MAXN];
    list<int> a[MAXN];
    set<pair<int, int>, greater<pair<int, int>>> st[MAXN];
    map<int, int> g[MAXN];
    
    void add(int ind, int x){
        a[ind].push_back(x);
        if (g[ind].count(x)) st[ind].erase({g[ind][x], x});
        st[ind].insert({++g[ind][x], x});
    }
    
    void del(int ind){
        int x(a[ind].back());
        st[ind].erase({g[ind][x], x});
        st[ind].insert({--g[ind][x], x});
        a[ind].pop_back();
    }
    
    void merge(int x, int y, int z){
        if (a[x].size() > a[y].size()){
            swap(a[x], a[z]);
            a[z].splice(a[z].end(), a[y]);
            swap(x, y);
        }else{
            swap(a[x], a[z]);
            a[z].splice(a[z].end(), a[y]);
        }
        for (auto i: st[x]){
            if (g[y][i.second]) st[y].erase({g[y][i.second], i.second});
            st[y].insert({g[y][i.second]+=i.first, i.second});
        }
        swap(st[z], st[y]);
        swap(g[z], g[y]);
    }
    
    bool check(int l, int r, int x){
        int res(0), siz(0);
        for (int i(l); i<=r; ++i){
            res += g[b[i]][x];
            siz += a[b[i]].size();
        }
        return (res<<1) > siz;
    }
    
    int solve(int l, int r){
        if (l == r){
            if (st[b[l]].empty() || (st[b[l]].begin()->first<<1) <= a[b[l]].size()) return -1;
            return st[b[l]].begin()->second;
        }
    
        int mid((l+r)>>1), res1(solve(l, mid)), res2(solve(mid+1, r));
        if ((~res1) && check(l, r, res1)) return res1;
        if ((~res2) && check(l, r, res2)) return res2;
        return -1;
    }
    
    signed main(){
        ios::sync_with_stdio(false);
        cin.tie(0);
        cout.tie(0);
    
        cin >> n >> q;
        for (int i(1), l; i<=n; ++i){
            cin >> l;
            for (int x; l; --l){
                cin >> x;
                add(i, x);
            }
        }
    
        for (int opt, x, y, z; q; --q){
            cin >> opt >> x;
            if (opt == 1){
                cin >> y;
                add(x, y);
            }else if (opt == 2) del(x);
            else if (opt == 3){
                for (int i(1); i<=x; cin >> b[i++]);
                cout << solve(1, x) << '\n';
            }else{
                cin >> y >> z;
                merge(x, y, z);
            }
        }
    
        return 0;
    }
    
    
    • 1

    信息

    ID
    7088
    时间
    1000ms
    内存
    1024MiB
    难度
    10
    标签
    递交数
    2
    已通过
    1
    上传者