Luogu P8496

题面

场外菜鸡 whker 听说你谷添加国赛新题立刻前来围观

首先我们看到本题对于众数的定义,很容易想到通过权值线段树求解。(类似这题,但本题不需要可持久化)

对于一个序列,我们维护一个 deque 和一个动态开点权值线段树。deque 表示序列本身,线段树每个节点记录值在 [l,r] 中的元素有多少个。

对于操作 1

线段树对应位置 +1,同时 deque push_back。无需多说。

对于操作 2

deque 中找到要删除的数,线段树对应位置 1,同时 deque pop_back。也无需多说。

对于操作 3

开一个 vector 存下所有被询问的节点,每次分别把左儿子和右儿子的总和求出来(相当于把合并序列变成合并节点),如果左边大于一半就向左边跳,如果右边大于一半就向右边跳,否则无解。

对于操作 4

合并两个序列,就是分别合并这两个序列的 deque 和线段树。

对于合并线段树,其实是非常简单的。要把线段树 T1,T2 合并,并将结果存入 T1,如果两棵树都存在能表示 [l,r] 的节点,只需要将两节点相加,否则选择存在的那个节点。

对于合并 deque,暴力一个一个 push_back 必然超时,因此考虑启发式合并,每次把长度较小的那个挨个 push 到长度较大的那个中去。需要注意的是要保证顺序,也就是应该 push_back 还是 push_front 的问题。这也是使用 deque 而不是 vector 的原因。

以上两个合并操作的时间复杂度在均摊后都是 O(nlogn) 的,严格证明需要势能分析,然而我不会,所以我只能靠直觉(悲)。

此外这题要开 long long,因为操作 3 不保证 x1,,xm 互不相同,可能加爆。(不开会 WA#16)

代码:

#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long ll;
constexpr int N = 5e5 + 5, M = 1e6;
struct node {
    int ls, rs; 
    ll cnt;
};
/*vector<node> nd;
int nnd(int& p) {
    if(p) return p;
    nd.push_back({0, 0, 0});
    return p = nd.size() - 1;
}*/
//用vector会出现申必的RE
node nd[N * 24];
int tot = -1;
#define nnd(p) p ? p : (p = ++tot)
struct SGT {
    deque<int> a;
    int rt;
    SGT() {
        a.clear();
        //nd.push_back({0, 0, 0});
        //rt = nd.size() - 1;
        rt = ++tot;
    }
    void add(int p, int l, int r, int loc, int val) {
        nd[p].cnt += val;
        if(l == r) return;
        int mid = (l + r) >> 1;
        if(loc <= mid) add(nnd(nd[p].ls), l, mid, loc, val);
        else add(nnd(nd[p].rs), mid + 1, r, loc, val);
    }
    void ins(int v) {
        a.push_back(v);
        add(rt, 1, M, v, 1);
    }
    void pop() {
        add(rt, 1, M, *a.rbegin(), -1);
        a.pop_back();
    }
}tr[N];
int n, q, ptr[M + 5];
int query(vector<int>& p, int l, int r, ll m) {
    if(l == r) return l;
    ll lsum = 0, rsum = 0;
    int mid = (l + r) >> 1;
    for(auto i : p) 
        lsum += nd[nd[i].ls].cnt, rsum += nd[nd[i].rs].cnt;
    if(lsum > m) {
        for(auto &i : p) i = nd[i].ls;
        return query(p, l, mid, m);
    }
    else if(rsum > m) {
        for(auto &i : p) i = nd[i].rs;
        return query(p, mid + 1, r, m);
    }
    else return -1;
}
int merge(int x, int y, int l, int r) {
    if(!x) return y; 
    if(!y) return x;
    nd[x].cnt += nd[y].cnt;
    if(l == r) return x;
    int mid = (l + r) >> 1;
    nd[x].ls = merge(nd[x].ls, nd[y].ls, l, mid);
    nd[x].rs = merge(nd[x].rs, nd[y].rs, mid + 1, r);
    return x;
}
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    cin >> n >> q;
    iota(ptr + 1, ptr + n + 1, 1);
    for(int i = 1; i <= n; ++i) {
        int l; cin >> l;
        for(int j = 1; j <= l; ++j) {
            int x; cin >> x; 
            tr[i].ins(x);
        }
        //cerr << i << ' ' << tot << endl;
    }
    while(q--) {
        int op, x, y, z;
        cin >> op >> x;
        if(op == 1) cin >> y, tr[ptr[x]].ins(y);
        else if(op == 2) tr[ptr[x]].pop();
        else if(op == 3) {
            vector<int> p;
            ll l = 0;
            while(x--) {
                int xx; cin >> xx;
                l += tr[ptr[xx]].a.size();
                p.push_back(tr[ptr[xx]].rt);
            }
            cout << query(p, 1, M, l >> 1) << endl;
        }
        else if(op == 4) {
            cin >> y >> z;
            if(tr[ptr[x]].a.size() > tr[ptr[y]].a.size()) {
                ptr[z] = ptr[x];
                for(auto i : tr[ptr[y]].a) tr[ptr[x]].a.push_back(i);
                tr[ptr[x]].rt = merge(tr[ptr[x]].rt, tr[ptr[y]].rt, 1, M);
            }
            else {
                ptr[z] = ptr[y];
                for(auto i = tr[ptr[x]].a.rbegin(); i != tr[ptr[x]].a.rend(); ++i) 
                    tr[ptr[y]].a.push_front(*i);
                tr[ptr[y]].rt = merge(tr[ptr[y]].rt, tr[ptr[x]].rt, 1, M);
            }
        }
    }
    return 0;
}

事后了解到这题开 106deque 在 CCF 的机子上是会 MLE 爆零的,其实完全可以用 list,再不济手写链表也行。

posted @   untitled0  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· 2 本地部署DeepSeek模型构建本地知识库+联网搜索详细步骤
点击右上角即可分享
微信分享提示