首页 > 其他分享 >P8496 [NOI2022] 众数

P8496 [NOI2022] 众数

时间:2023-05-13 15:46:16浏览次数:33  
标签:rt sz head P8496 int lst 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;
}

标签:rt,sz,head,P8496,int,lst,NOI2022,众数
From: https://www.cnblogs.com/lingspace/p/p8496.html

相关文章

  • NOI2022 模拟赛合集【kel.ac.cn】
    RoundXLIX开场看B想了3h假回去想A1h切。A.守序划分问题容易想到一个必要条件,对于任意集合\(S\),需要满足\(\max_{i\inS}A_i>\min_{i\notinS}A_i\)。然后用你的大脑构造一下发现这东西也充分。于是思考如何计数,即不能将数列割裂成两个部分。于是考虑动态规划。设\(f[i,j]......
  • 洛谷 P8367 - [LNOI2022] 盒(组合数学)
    设\(a\)数组的前缀和为\(s_i\),\(b\)数组的前缀和为\(t_i\),那么根据模拟费用流或者贪心的思想,每一条边经过的次数即为\(|s_i-t_i|\),因此非常trivial的做法是转换贡献体,枚举每种方案下每条边被经过的次数,然后乘以\(w_i\)求和,具体来说:\[ans=\sum\limits_{i=1}^{n-1}\sum\l......
  • 数据计算--众数-方差
    1.方法说明: 2.三科成绩的众数: 3.行中的众数: 4.指定列的众数: 5.方差的计算,方差越小数据越稳定: ......
  • Luogu P8496
    题面场外菜鸡whker听说你谷添加国赛新题立刻前来围观首先我们看到本题对于众数的定义,很容易想到通过权值线段树求解。(类似这题,但本题不需要可持久化)对于一个序列,我们维护一个deque和一个动态开点权值线段树。deque表示序列本身,线段树每个节点记录值在\([l,r]\)中的元素......
  • python计算list的均值,方差,众数,中位数的最好方法
    可以使用Python的统计模块statistics来计算列表的均值、方差、中位数等,下面是一些示例代码:importstatistics#定义一个列表my_list=[1,2,3,4,5]#计算均值mean=statistics.mean(my_list)print("均值:",mean)#计算方差variance=statistics.variance(m......
  • 501. 二叉搜索树中的众数
    给你一个含重复值的二叉搜索树(BST)的根节点root,找出并返回BST中的所有众数(即,出现频率最高的元素)。如果树中有不止一个众数,可以按任意顺序返回。假定BST满足如下定义:结点左子树中所含节点的值小于等于当前节点的值结点右子树中所含节点的值大于等于当前节点的值左......
  • 求众数
    #include<stdio.h>intnum=0;  //存放众数intmaxcount=0;   //存放重数voidsplit(inta[],intlow,inthigh,int&mid,int&left,int&right){  mid=(low+high)/2;  for(left=mid;left<=high;left--)  {    if(a[left]!=a[mid]){  ......
  • 二叉搜索树中的众数
    LeetCode|501.二叉搜索树中的众数给你一个含重复值的二叉搜索树(BST)的根节点root,找出并返回BST中的所有众数(即,出现频率最高的元素)。如果树中有不止一个众数,可以按任意顺序返回。假定BST满足如下定义:结点左子树中所含节点的值小于等于当前节点的值结点右子树中所......
  • day21 打卡530.二叉搜索树的最小绝对差 501.二叉搜索树中的众数 236. 二叉树的最近公
    day21打卡530.二叉搜索树的最小绝对差501.二叉搜索树中的众数236.二叉树的最近公共祖先530.二叉搜索树的最小绝对差530题目链接1.递归法——使用双指针。因为是二叉......
  • 代码随想录21 530.二叉搜索树的最小绝对差 | 501.二叉搜索树中的众数 | 236. 二
    530. 二叉搜索树的最小绝对差给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值 。差值是一个正数,其数值等于两值之差的绝对值。classS......