Y
K
N
U
F

题解 | P8496 [NOI2022] 众数

P8496 [NOI2022] 众数
很少口口口形容一个题目。一道题目,我改换了三种假做法 \(\dots\)

抱怨部分(不想看直接往下翻就可以)

最开心的部分:

光速读题,高兴于一眼看出用链表维护原序列,权值线段树合并完成答案的维护,直接写出了非常 RE 的指针版,故改掉指针(0.5h)。

最上流的解法:

发现限制了 \(\sum C\) 的范围,想当然以为它的意思是线段树合并很安全,故考虑直接对于每一个操作三建立一颗临时树,在那上面查询,发现样例一下子就过了,高兴得很,提交,20 TLE + WA,不信邪,调试约半辈子(1.5h),来到下午,有人讲这道题。说这种做法很假,很破防,非要调出来这种做法,发现错在操作三节点没克隆而是直接修改了,遂大骂而改。然而,显然,操作三不懂人情世故,#11 12 13 17 18 19 20 并不会放过去这种躺尸做法。

最正确的做法(自认为):

结合同学讲解,注意到若一个数是绝对众数,则每次选择一点有大于 1/2 的概率选对,所以选的越多就越对,所以,我,直接,假设答案为 $ x = rand() \mod {n + q}$,使用最纯粹的随机拿下整整 10pts。

最天才的理解:

这样子做本来就只是随便搞搞,错了当然,要随机至少得先是里面的数才行,所以当然是在这些数里面随便找拉!~(T、Wa飞了),再次找到同学询问,同学说只要前11个数就可以了,所以高兴的更改,但还是错的,为什么呢。
阿,是我们天才的 旅游美食 听错了呢!实际上是只要前11个树的局部最优值。。。

总之虽然波折,至少该对了罢。。。。。。

不抽象的讲述正解

正确的做法大概已经在上面说过了。不喜欢看我牢骚可以直接跳到这里,简单概括就是用链表维护原序列,权值线段树合并完成答案的维护。权值线段树要维护本个链表中的众数(不是绝对众数),对于 \(1、2\) 直接加减修改就行,对于 \(3\) 则找出前几个数的局部最优值进行 \(check\),若存在满足绝对众数的数则会最多有 \(1/2^{k}\) 的概率出错。大概是前 11 个数能保证正确性("二分答案"出的结论)。对于 \(4\) 显然是链表和权值线段树的合并,注意合并前判定是不是空序列,否则会 75pts WA。于是题目结束,实现就放在下面了。注释有但不多(懒)。

#include <bits/extc++.h>
#define int long long
#define e_ putchar_unlocked(' ')
#define en_ putchar_unlocked('\n')
using namespace std;
inline int in() {
    int n = 0; char p = getchar_unlocked();
    while (p < '-') p = getchar_unlocked();
    bool f = p == '-' ? p = getchar_unlocked() : 0;
    do n = (n<<1) + (n<<3) + (p ^ 48), p = getchar_unlocked();
    while (isdigit(p));
    return f ? -n : n;
    return n;
}
template <typename T> inline void in(T &n) { n = in();}
inline void out(int x) {
    if(x < 0) putchar_unlocked('-'), x = -x;
    if(x >= 10) out(x/10);
    putchar_unlocked(x % 10 + '0');
}
constexpr int N = 1000000 + 10; const char me[] = "Ciallo~(∠・ω< )⌒ ★ ";
struct node {
    int l, r;
    int same, i;//same 指区间内出现最多的数出现的次数,i是内个数
} nod[N * 25];
int top;
inline int nnode() { top ++ ;return top;}
inline int clone(int t) {
    nod[++top] = nod[t];
    return top;
}
inline void up(int u) {
    nod[u].same = max(nod[nod[u].l].same ,nod[nod[u].r].same);
    if(nod[u].same == nod[nod[u].l].same) nod[u].i = nod[nod[u].l].i;
    else nod[u].i = nod[nod[u].r].i;
}
#define ul nod[u].l
#define ur nod[u].r
#define usame nod[u].same
#define lsame nod[ul].same
#define rsame nod[ur].same
inline void insert(int&u, int l, int r, int val) { // 插入 - 权值线段树
    if(!u) u = nnode();
    if(l == r) { usame ++; nod[u].i = l; return;}
    int m = (l+r) >> 1;
    if(m >= val) insert(ul, l, m, val);
    else insert(ur, m+1, r, val);
    up(u);
}
inline void delate(int u, int l, int r, int val) { // 删除 - 权值线段树
    if(l == r) { usame --; return;}
    int m = (l+r) >> 1;
    if(m >= val) delate(ul, l, m, val);
    else delate(ur, m+1, r, val);
    up(u);
}
struct mylist {
    int data;
    int pre, nxt;//链表
    inline bool end() { return !nxt;}
    inline bool begin() { return !pre;}
} lis[N * 20]; 
int topl;
inline int nlist() {
    return ++topl;
}
inline void ins(int u, int&me) { // 插入 - 链表
    if(!me) { me = nlist(); lis[me].data = u; return;}
    while(!lis[me].end()) me = lis[me].nxt;
    lis[me].nxt = nlist();
    lis[lis[me].nxt].data = u, lis[lis[me].nxt].pre = me;
    me = lis[me].nxt;
}
inline void del(int&me) { // 删除 - 链表
    lis[me].nxt = 0;
    me = lis[me].pre;
    lis[lis[me].nxt].pre = 0;
    lis[me].nxt = 0;
}
struct tree {
    int rot, siz;// siz 用链表维护会更快
    int en, be;//链表头尾
    inline void addnum(int val) {
        insert(rot, 0, N - 1, val);
        ins(val, en);
        siz++;
        if(siz == 1) be = en;
    }
    inline void delnum() {
        while(!lis[en].end()) en = lis[en].nxt;
        delate(rot, 0, N - 1, lis[en].data);
        del(en);
        siz--;
    }
} tr[N]; // 方便维护多棵树
inline void merge(int&a, int&b, int l, int r) {
    if(!a) { a = b; return;}
    if(!b) return;
    if(l == r) { nod[a].same += nod[b].same; return;} 
    int m = (l+r) >> 1;
    merge(nod[a].l, nod[b].l, l, m);
    merge(nod[a].r, nod[b].r, m+1, r);
    up(a);
}
inline void merge_tr(tree a, tree b, tree&A) { // 合并序列
    if(!a.siz && !b.siz) return;
    if(a.siz == 0) { A = b; return;} // 特判!!!
    if(b.siz == 0) { A = a; return;}
    A = a;
    merge(A.rot ,b.rot , 0, N-1);
    while(!lis[A.en].end()) A.en = lis[A.en].nxt;
    while(!lis[b.en].end()) b.en = lis[b.en].nxt;
    while(!lis[b.be].begin()) b.be = lis[b.be].pre;
    lis[A.en].nxt = b.be;
    lis[b.be].pre = A.en;
    A.en = b.en;
    A.siz += b.siz;
}
inline int query(int u, int l, int r, int val) {
    if(l == r) return nod[u].same;
    int m = (l+r) >> 1;
    if(m >= val) return query(ul, l, m, val);
    else return query(ur, m+1, r, val);
}
inline int que(int y) { // 万恶的询问
    int msz = 0;
    vector<int>tmp;
    for(int i = 1; i <= y; i ++) tmp.push_back(in());
    for(int v : tmp) msz += tr[v].siz;
    msz = msz / 2 + 1;
    int cnt = 0;
    for(int v : tmp) {
        if(cnt++ > 13) break;
        int sm = 0, x = nod[tr[v].rot].i;
        for(int g : tmp) {
            sm += query(tr[g].rot, 0, N-1, x);
        }
        if(sm >= msz) return x;
    }
    return -1;
}
struct momo {
    void excute(const char *me) {
        int n = in(), q = in();
        for(int i = 0; i <= n+q; i++) tr[i].rot = nnode();
        for(int i = 1; i <= n; i ++) {
            int l = in();
            for(int j = 1, x; j <= l; j ++) {
                in(x);
                tr[i].addnum(x);
            }
        }
        // tr[1].addnum(0);
        // merge_tr(tr[1], tr[2], tr[5]);
        // tr[5].addnum(223);
        // for(int i = 1; i <= 124; i++) {
        //     tr[5].addnum(124);
        // }
        // for(int i = 1; i <= 123; i ++) tr[3].addnum(i);
        // merge_tr(tr[3], tr[5], tr[112]);
        // out(tr[112].siz);
        for(int i = 1, op, x, y, j; i <= q; i ++) {
            in(op);
            switch(op) {
                case 1 :{
                    in(x), in(y);
                    tr[x].addnum(y);
                    break;
                }
                case 2 :{
                    in(x);
                    tr[x].delnum();
                    break;
                }
                case 3 :{
                    in(y);
                    out(que(y)), en_;
                    break;
                }
                case 4 :{
                    in(x), in(y), in(j);
                    merge_tr(tr[x], tr[y], tr[j]);
                    break;
                }
            }
        }
    }
} world;
signed main() { 
    world.excute(me);
}
posted @ 2025-07-22 22:13  樓影沫瞬_Hz17  阅读(22)  评论(0)    收藏  举报