首页 > 其他分享 >Luogu P8496

Luogu P8496

时间:2023-04-24 20:24:21浏览次数:42  
标签:rt P8496 int Luogu nd tr return ptr

题面

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

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

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

对于操作 \(1\):

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

对于操作 \(2\):

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

对于操作 \(3\):

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

对于操作 \(4\):

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

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

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

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

此外这题要开 long long,因为操作 \(3\) 不保证 \(x_1, \ldots, x_m\) 互不相同,可能加爆。(不开会 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;
}

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

标签:rt,P8496,int,Luogu,nd,tr,return,ptr
From: https://www.cnblogs.com/untitled0/p/luogu-P8496.html

相关文章

  • Luogu P3336
    因为我也是看了大佬的题解才写的(第一问),自认为自己讲得不可能比他们再好了,但是因为好多第二问的题解都被hack了,所以这里详细讲一下第二问的正确做法。初中平几课堂开课啦其实思路很简单,利用贪心的思想,能往上走就往上走,能走多高就走多高,来看这个图:点\(A\)是当前点,点\(B\)是......
  • Luogu P1999
    题目传送门初中数学老师在平面几何的第一节课就和我们说过:点动成线,线动成面,面动成体。即,由\(i-1\)维元素变化到\(i\)维的过程,就可以认为是将\(i-1\)维物体沿第\(i\)个方向平移的过程。因此我们考虑一个二维的正方形平移得到三维的正方体的过程:如果我们以平面的个......
  • Luogu_P1613 跑路 题解
    发现和最短路差不多,不过不能朴素的跑最短路。考虑对于每两个相隔\(2\)的整数次幂的点建边,在这个新图上跑最短路就是答案。设\(f_{i,j,k}\)表示从点\(i\)跳\(2^k\)步能否到点\(j\),转移方程就是一个普通的倍增。如果点\(i\)和点\(j\)可以一步到达,那么就在新图上建一条......
  • luogu P3308 [SDOI2014]LIS
    题面传送门涨知识了,第一次知道网络流删边不用全图重跑。首先我们先跑一个暴力dp,出\(f_i\)表示以\(i\)结尾的最长上升子序列长度。然后我们将其按照这个dp值分层,相......
  • 【NOIP2015】【Luogu2615】神奇的幻方(模拟填数)
    problem给一定n*n的矩阵,要求填上1~n*n的数,使之每行、列、对角线的和都相等。n为奇数时,按如下步骤构建:1.若(K−1)在第一行但不在最后一列,则将K填在最后一行,(K−1)所在列的右......
  • luogu P7520 [省选联考 2021 A 卷] 支配
    题面传送门自己瞎胡的支配树,可能是错的(大雾首先我们可以证明,支配关系成树。考虑一个点\(x\)的两个受支配点\(y,z\),这两个点应该在一条路径上,如果\(y,z\)之间没有支......
  • luogu P9120 [春季测试 2023] 密码锁
    题面传送门题目中明摆着让你对\(k\)不同的情况讨论,并且难度应该是递增的。Section1:\(k=1\)应该不用我教你怎么做吧Section2:\(k=2\)最大值最小下意识二分转化成判......
  • luogu P7599 [APIO2021] 雨林跳跃
    题面传送门我成功了,我不再是以前那个我了!我们发现部分分里面有个单点跳到单点,尝试考虑这个部分分。一个点有两个点可以跳,贪心地想,如果我先跳了比较矮的那个,那么再一步能......
  • luogu P4566 [CTSC2018]青蕈领主
    题面传送门最后这个转化非常牛逼啊!首先我们可以证明:一个合法的序列中,这样的极长连续区间不会相交。Proof:如果相交了,说明相交的区间也是一段连续区间,而每个区间不相交的......
  • luogu「P4313」文理分科 解题报告
    题目描述文理分科是一件很纠结的事情!(虽然看到这个题目的人肯定都没有纠结过)小P所在的班级要进行文理分科。他的班级可以用一个\(n\timesm\)的矩阵进行描述,每个格子......