题目描述

对于一个序列,定义其众数为序列中出现次数严格大于一半的数字。注意该定义与一般的定义有出入,在本题中请以题面中给出的定义为准。
一开始给定 nnn 个长度不一的正整数序列,编号为 1∼n1 \sim n1∼n,初始序列可以为空。这 nnn 个序列被视为存在,其他编号对应的序列视为不存在。 有 qqq 次操作,操作有以下类型:

  • 1xy1 \ x \ y1 x y:在 xxx 号序列末尾插入数字 yyy。保证 xxx 号序列存在,且 1≤x,y≤n+q1 \le x, y \le n + q1≤x,y≤n+q。
  • 2x2 \ x2 x:删除 xxx 号序列末尾的数字,保证 xxx 号序列存在、非空,且 1≤x≤n+q1 \le x \le n + q1≤x≤n+q。
  • 3mx1x2xm3 \ m \ x_1 \ x_2 \ x_m3 m x1​ x2​ xm​:将 x1,x2,…,xmx_1, x_2, \ldots, x_mx1​,x2​,…,xm​ 号序列顺次拼接,得到一个新序列,并询问其众数。如果不存在满足上述条件的数,则返回 −1-1−1。数据保证对于任意 1≤i≤m1 \le i \le m1≤i≤m,xix_ixi​ 是一个仍然存在的序列,1≤xi≤n+q1 \le x_i \le n + q1≤xi​≤n+q,且拼接得到的序列非空。注意:不保证 x1,…,xm{x_1, \ldots, x_m}x1​,…,xm​ 互不相同,询问中的合并操作不会对后续操作产生影响。
  • 4x1x2x34 \ x_1 \ x_2 \ x_34 x1​ x2​ x3​:新建一个编号为 x3x_3x3​ 的序列,其为 x1x_1x1​ 号序列后顺次添加 x2x_2x2​ 号序列中数字得到的结果,然后删除 x1,x2x_1, x_2x1​,x2​ 对应的序列。此时序列 x3x_3x3​ 视为存在,而序列 x1,x2x_1, x_2x1​,x2​ 被视为不存在,在后续操作中也不会被再次使用。保证 1≤x1,x2,x3≤n+q1 \le x_1, x_2, x_3 \le n + q1≤x1​,x2​,x3​≤n+q、x1≠x2x_1 \ne x_2x1​=x2​、序列 x1,x2x_1, x_2x1​,x2​ 在操作前存在、且在操作前没有序列使用过编号 x3x_3x3​。

输入输出格式

输入格式

输入的第一行包含两个正整数 nnn 和 qqq,分别表示数列的个数和操作的次数,保证 n≤5×105n \le 5 \times {10}^5n≤5×105、q≤5×105q \le 5 \times {10}^5q≤5×105。
接下来 nnn 行,第 iii 行表示编号为 iii 的数列。
每一行的第一个非负整数 lil_ili​ 表示初始第 iii 号序列的数字个数,接下来有 lil_ili​ 个非负整数 ai,ja_{i,j}ai,j​ 按顺序表示数列中的数字。
假定 Cl=∑liC_l = \sum l_iCl​=∑li​ 代表输入序列长度之和,则保证 Cl≤5×105C_l \le 5 \times {10}^5Cl​≤5×105、ai,j≤n+qa_{i,j} \le n + qai,j​≤n+q。
接下来 qqq 行,每行若干个正整数,表示一个操作,并按照题面描述中的格式输入。
假定 Cm=∑mC_m = \sum mCm​=∑m 代表所有操作 333 需要拼接的序列个数之和,则保证 Cm≤5×105C_m \le 5 \times {10}^5Cm​≤5×105。

输出格式

对于每次询问,一行输出一个整数表示对应的答案。

solution

这个题:我们可以不是很显然地知道:众数为中位数。。。。。

浅浅地证明一下啊:

若众数在最左边

众数为序列中出现次数严格大于一半的数字

∴a[1→ed]=same,ed−1+1(len众数)≥12lenalla[1\to ed]=same,ed-1+1(len_{众数})≥\frac{1}{2}len_{all}a[1→ed]=same,ed−1+1(len众数​)≥21​lenall​

∴1≤mid<ed1≤mid<ed1≤mid<ed

∴a[mid]=a[1]a[mid]=a[1]a[mid]=a[1]

即中位数=众数

若众数在最右边,同理可得中位数=众数

若在中间:

若众数为a[w]a[w]a[w]

len>midlen>midlen>mid

∴a[w→w+len]=samea[w\to w+len]=samea[w→w+len]=same

又∵w+len≤nw+len≤nw+len≤n

∴w<midw<midw<mid

又因为w+len>midw+len>midw+len>mid

∴a[mid]=a[w]a[mid]=a[w]a[mid]=a[w]

综上所述,众数为中位数

证毕。

so,我们可以开一个权值线段树来统计序列中数的个数

用链表来存序列。。。。。。。。。。

权值线段树

权值线段树即一种线段树,以序列的数值为下标
权值线段树维护一列数中数的个数

也就是说,我们的权值线段树就是用线段树维护了一堆桶。

这就是权值线段树的概念。

权值线段树维护的是桶,按值域开空间,维护的是个数

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=4e6+2;
int n;//初始 有多少链表
int q;//询问个数
int m;//询问3链表的个数
int que[N];// 询问3的第i个链表的id
int lst[N];//询问3的第i个链表的rt值
int lim;//总共有多少链表
int hd[N];//i链表的头头
int tl[N];//i链表的尾巴
int pre[N];//i的前面一个
int val[N];//第i个数的值
int tot;//加入数的id
ll sz[N];//i链表的size大小
int rt[N];//i链表在线段树上的rt
int lc[N],rc[N];//线段树i的左右儿子
int ttt;//线段树节点(动态开点)
ll sum[N];//i的个数(线段树)
int x,op,id,y;
void addlink(int id,int x){//在链表后加上x if(!sz[id]) hd[id]=x;//x就是id链表的第一个元素(ta就是头头) sz[id]++;//链表大小更新 pre[x]=tl[id];//x的前一个就是id链表之前的尾巴 tl[id]=x;//新尾巴
}
void dellink(int id){//删去链表末尾 sz[id]--;// 链表大小更新if(!sz[id]) hd[id]=0;//id链表没了。。 tl[id]=pre[tl[id]];//尾巴就是尾巴的前一个
}
void link(int x,int y,int id){//链表合并 if(sz[x])hd[id]=hd[x];//id的头头就是x的头头 else hd[id]=hd[y];//但是如果xGG了话就是y的头头了 if(sz[y])tl[id]=tl[y];//id的尾巴就是y的尾巴  else tl[id]=tl[x];//但是如果yGG了话就是x的尾巴了 sz[id]=sz[x]+sz[y];//id的大小就是x和y加起来 if(hd[y]) pre[hd[y]]=tl[x];//xy首位拼接
}
void pushup(int p){sum[p]=sum[lc[p]]+sum[rc[p]];//sum数组更新
}
void update(int &p,int l,int r,int w,ll v){if(!p){p=++ttt; //动态开点 } //懂? if(l==r){//找到啦 sum[p]+=v;return ;}int mid=(l+r)>>1;if(w<=mid) update(lc[p],l,mid,w,v);//在左边 else update(rc[p],mid+1,r,w,v);//在右边 pushup(p);
}
int merge(int x,int y,int l,int r){if(!x||!y) return (x+y);//有一个已经GG了 if(l==r){// sum[x]+=sum[y];return x;}int mid=(l+r)>>1;lc[x]=merge(lc[x],lc[y],l,mid);rc[x]=merge(rc[x],rc[y],mid+1,r);pushup(x);//合并合并 afasdfasd return x;
}
ll getsum(int p,int l,int r,int L){//L出现的次数 if(!p) return 0;if(l==r) return sum[p];//you got it! int mid=(l+r)>>1;if(L<=mid) return getsum(lc[p],l,mid,L);return getsum(rc[p],mid+1,r,L);
}
int query(int l,int r,int k){//二分 中位数 if(l==r) return l;ll tmp=0;for(int i=1;i<=m;i++){tmp+=sum[lc[lst[i]]];}int mid=(l+r)>>1;if(tmp>=k){//在← for(int i=1;i<=m;i++){lst[i]=lc[lst[i]];} return query(l,mid,k);}else{for(int i=1;i<=m;i++){lst[i]=rc[lst[i]];} return query(mid+1,r,k-tmp);}
}
int main(){scanf("%d%d",&n,&q);lim=n+q;for(int i=1;i<=n;i++){scanf("%d",&m);for(int j=1;j<=m;j++){scanf("%d",&x);val[++tot]=x;addlink(i,tot);update(rt[i],1,lim,x,1); }}while(q--){scanf("%d",&op);if(op==1){scanf("%d%d",&id,&x);val[++tot]=x;addlink(id,tot);//链表末尾加上x(val[tot]) update(rt[id],1,lim,x,1);//cnt[x]+1 } else if(op==2){scanf("%d",&id);update(rt[id],1,lim,val[tl[id]],-1);//cnt[val[链表id末尾tl[id]]]-1 dellink(id);//删去链表末尾 } else if(op==3){scanf("%d",&m);ll all=0;//合并后数组长度for(int i=1;i<=m;i++){scanf("%d",&que[i]);//第i个数组lst[i]=rt[que[i]];//第i个数组的头头all+=sz[que[i]];//总长 } int xx=query(1,lim,(all+1)>>1);ll tmp=0;//tmp:中位数xx出现的个数 for(int i=1;i<=m;i++){tmp+=getsum(rt[que[i]],1,lim,xx);//xx在第i个数组中出现的次数 }if((tmp<<1)>all) printf("%d\n",xx);//ok啊 else printf("-1\n"); //不ok } else{scanf("%d%d%d",&x,&y,&id);link(x,y,id);//合并两链表 rt[id]=merge(rt[x],rt[y],1,lim); //合并线段树 }}return 0;
}

完结撒花❀
★,°:.☆( ̄▽ ̄)/$:.°★

[NOI2022] 众数 题解相关推荐

  1. P4062 [Code+#1]Yazid 的新生舞会(分治做法)

    P4062 [Code+#1]Yazid 的新生舞会 题意: 给出一个序列,求有多少个子区间满足众数的出现次数大于区间长度的一半. 出现次数大于区间长度的一般我们称之为绝对众数 题解: 分治做法 对于 ...

  2. NOI2022联合省选 题解

    NOI2022联合省选 题解 day1的题解咕咕咕了,有空再写吧(好吧,看到这么长的题面+大模拟就没有写题解的欲望了-- 注:非官方题解,不保证绝对正确,做法不一定是官方做法 注2:代码请见这篇blo ...

  3. leetcode 501. 二叉搜索树中的众数(Java版)

    题目 https://leetcode-cn.com/problems/find-mode-in-binary-search-tree/ 题解 中序遍历二叉搜索树,可以得到一个有序序列. 遍历这个有序 ...

  4. P4062 [Code+#1]Yazid 的新生舞会(区间绝对众数+分治/树状数组维护高维前缀和)

    P4062 [Code+#1]Yazid 的新生舞会 杭电多校懂得都懂 Code1 分治 比较喜欢分治的做法,非常好写.skylee大佬题解 首先对于任何一个区间来说,由于两个端点不确定性非常难以一次 ...

  5. 题解 LGOJ P4168 【[Violet]蒲公英】

    呃,这算是我自己做出来的第一道黑题吧... 感觉巨水虽然我吸氧才过QAQ 来一波题解- 分块板子题 1.题意及思路 首先,发现题目是要求静态区间众数 暴力的想法: ​ 先离散化,对于每个询问区间\([ ...

  6. LeetCode题解目录

    最新更新于2020.11.27 前往LeetCode主页. 前往GitHub源码.(服务器原因,暂停同步.) 前往码云主页. 已解决 456/1878 - 简单353 中等 90 困难 13 2020 ...

  7. 算法题-----题目、题解、个人算法、个人思考

    算法题-----题目.题解.个人算法.个人思考,项目说明]这里记录算法题目,这里记录:算法题目简单描述,暂存对于这道题的SK_cache(自己的卡点.自己的思路.自己对这道题的理解,自己的疑问.自己对 ...

  8. 一句话题解(20170801~20170125)

    8.1 bzoj 4720 noip2016 换教室 floyd预处理+期望(薛定谔的猫) bzoj 4318 OSU! 三次函数期望值 从一次.二次推得 8.2 bzoj 1076 状压+期望DP ...

  9. 算法与数据结构+LeetCode题解-Js版

    LeetCode题解Js版 https://webbj97.github.io/leetCode-Js/ 题外话 LeetCode题解:传送门 前端笔记:传送门 项目背景 旨在提高自己对算法的理解,将 ...

最新文章

  1. php函数细节_php strip_tags()函数使用注意细节
  2. python主程序流程图_python中的图形渲染(流程图可视化)
  3. 101. Leetcode 139. 单词拆分 (动态规划-完全背包)
  4. MATLAB实战系列(三十)-MATLAB之M码正弦稳态电路建模仿真原理
  5. VirtualBox 图形化界面
  6. IBM挺进云计算 自家内采用私有云模式
  7. 如何在Windows 10上使用Microsoft Defender扫描文件或文件夹中的恶意软件
  8. Linux TCP server系列(4)-浅谈listen与大并发TCP连接
  9. C++工作笔记-作用域( :: )的另一种玩法
  10. nec的reset文件摘要
  11. 认真学习设计模式之适配器模式(Adapter Pattern)/包装器模式
  12. 浙江大学 工程伦理 第十二单元测试答案
  13. 计算机学报和c类会议,ccf期刊目录_ccf_ccf a类
  14. PBOOTCMS后台模板管理修改插件
  15. php 之session 进行时
  16. 什么是NETBIOS?
  17. Direct3D显示视频
  18. FleaPHP 开发指南 - 2. FleaPHP 中的 MVC 模式
  19. pikachu SQL注入 (皮卡丘漏洞平台通关系列)
  20. 66-甲说乙说谎,乙说丙说谎,丙说甲乙说谎

热门文章

  1. android 网络运营商名称显示规则(锁屏界面,下拉列表)
  2. 计算机类(0809)求职经验
  3. MySql函数 - DATE_ADD()函数
  4. uni-app知识点整理
  5. 判断TTS语音朗读是否结束
  6. 程序员常用十大算法(四):KMP算法 与 暴力匹配算法 解决字符串匹配问题
  7. Tungsten Fabric入门宝典丨8个典型故障及排查Tips
  8. oracle错误号提示ORA-
  9. CNCC 2018 今日开幕,五场特邀报告引爆「大数据推动数字经济」| CNCC 2018
  10. 开发实况4.1.linux相关-CRT连接虚拟机提示用户名或密码错误