首页 > 其他分享 >数据结构维护 mex 总结

数据结构维护 mex 总结

时间:2023-09-06 14:34:47浏览次数:42  
标签:总结 int void mid wz build 数据结构 mex

P4137

solution 1:

我最初做这题是莫队,这是一道练习莫队+值域分块的好题。

莫队的时候记录两个东西,\(b_i\) 表示 \(i\) 在当前出现的次数,\(c_i\) 表示值域第 \(i\) 块中有出现的数的个数

显然 \(b,c\) 都是可以满足莫队 \(O(1)\) 移动指针。

而后查询枚举值域块,令 \(len_i\) 表示值域第 \(i\) 块大小。如果找到第一个 \(c_i\neq len_i\),则 mex 一定在第 \(i\) 块中,

这时枚举块中元素判断出现次数是否 \(>0\) 即可。回答询问是 \(O(\sqrt V)\) 的。

\(n,V\) 同阶,总复杂度是 \(O(n\sqrt n)\) 的。

$\texttt{code}$
#include<bits/stdc++.h>
#define fr(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);
using namespace std;
const int N=2e5+6,len=450;
#define bl(x) (x-1)/len+1
struct node
{
	int l,r,id;
	bool operator<(node x){return (bl(l)^bl(x.l))?(l<x.l):((bl(l)&1)?r<x.r:r>x.r);}
}a[N];
int n,m,b[N],cnt[len],cnt1[N],L[len],R[len],ans[N];
inline void add(int x){(!cnt1[b[x]])&&(cnt[bl(b[x])]++);cnt1[b[x]]++;}
inline void del(int x){(cnt1[b[x]]==1)&&(cnt[bl(b[x])]--);cnt1[b[x]]--;}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=bl(N-5);i++) L[i]=(i-1)*len+1,R[i]=i*len;R[bl(N-5)]=N-5; 
	for(int i=1;i<=n;i++) scanf("%d",&b[i]),b[i]++;
	int l,r;
	for(int i=1;i<=m;i++) scanf("%d%d",&l,&r),a[i]={l,r,i};
	sort(a+1,a+1+m);l=1;r=0;
	for(int i=1;i<=m;i++)
	{
		while(l>a[i].l) add(--l);
		while(r<a[i].r) add(++r);
		while(l<a[i].l) del(l++);
		while(r>a[i].r) del(r--);
		int wz=0;
		for(int j=1;j<=bl(N-5);j++) if(cnt[j]!=R[j]-L[j]+1){wz=j;break;}
		for(int j=L[wz];j<=R[wz];j++) if(!cnt1[j]){ans[a[i].id]=j;break;}
	}
	for(int i=1;i<=m;i++) printf("%d\n",ans[i]-1);
	return 0;
}

solution 2:

这时洛谷第一篇题解的做法。

从左往右扫一遍,在权值线段树(要可持久化一下)上修改当前权值对应的最后一次出现的位置为当前位置。

查询区间 \([l,r]\) 时,答案就是第 \(r\) 棵线段树上,第一个最后一次出现的位置小于 \(l\) 的权值。

其实也可以不可持久化的,只需把询问离线一下。(但是我下面讲的就算半在线了)

复杂度 \(O(n\log n)\)。

$\texttt{code}$
#include<bits/stdc++.h>
#define LL long long
#define fr(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);
using namespace std;
const int N=2e5+5;
int n,m,V,a[N],tot,rt[N];
struct node{int ls,rs,x;}b[N*25];
void build(int l,int r,int &wz)
{
	b[wz=++tot]={0,0,0};if(l==r) return;
	int mid=(l+r)>>1;build(l,mid,b[wz].ls);build(mid+1,r,b[wz].rs);
}
void updata(int l,int r,int &wz,int wz1,int x,int y)
{
	b[wz=++tot]=b[wz1];if(l==r) return b[wz].x=y,void();
	int mid=(l+r)>>1;
	if(x<=mid) updata(l,mid,b[wz].ls,b[wz1].ls,x,y);
	else updata(mid+1,r,b[wz].rs,b[wz1].rs,x,y);
	b[wz].x=min(b[b[wz].ls].x,b[b[wz].rs].x);
}
int query(int l,int r,int wz,int x)
{
	if(l==r) return l;int mid=(l+r)>>1;
	if(b[b[wz].ls].x<x) return query(l,mid,b[wz].ls,x);
	return query(mid+1,r,b[wz].rs,x);
}
int main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i],V=max(V,a[i]);V+=3;build(1,V,rt[0]);
	for(int i=1;i<=n;i++) updata(1,V,rt[i],rt[i-1],a[i]+1,i);
	while(m--)
	{
		int l,r;cin>>l>>r;
		cout<<query(1,V,rt[r],l)-1<<"\n";
	}
	return 0;
}

CF1436E

我们考虑是否存在一个子区间,满足其 mex 为 \(x\)。

首先,这个子区间里面必须要没有 \(x\),

于是我们首先把数组中所有的 \(x\) 找出来,这些 \(x\) 把数组分成了若干段,我们要的子数组必须不能跨越这些段。

由于 \(t\) 个数会把数组分成了 \(O(t)\) 段。于是对于所有数,我们总共会分 \(O(n)\) 段。

于是我们对每个段,按上一题的方法查询 mex,判断是否 \(\text{mex}=x\),就可以知道是否存在 mex 为 \(x\) 的子区间。

若 \(x\) 是第一个不存在这样子区间的,那么答案就是 \(x\)。

设 \(\max(a)=V\),则答案的范围为 \([1,V+2]\),需要多查两个数,对于没出现过的数查询 \([1,n]\) 的 mex 是否为它即可。具体细节看代码。

复杂度取决于你上一题的写法,如果莫队就是 \(O(n\sqrt n)\),可持久化线段树就是 \(O(n\log n)\)。

$\texttt{code}$

#include<bits/stdc++.h>
#define LL long long
#define fr(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);
using namespace std;
const int N=1e5+5;
int n,V,a[N],tot,rt[N];
vector<int>g[N];
struct node{int ls,rs,x;}b[N*25];
void build(int l,int r,int &wz)
{
	b[wz=++tot]={0,0,0};if(l==r) return;
	int mid=(l+r)>>1;build(l,mid,b[wz].ls);build(mid+1,r,b[wz].rs);
}
void updata(int l,int r,int &wz,int wz1,int x,int y)
{
	b[wz=++tot]=b[wz1];if(l==r) return b[wz].x=y,void();
	int mid=(l+r)>>1;
	if(x<=mid) updata(l,mid,b[wz].ls,b[wz1].ls,x,y);
	else updata(mid+1,r,b[wz].rs,b[wz1].rs,x,y);
	b[wz].x=min(b[b[wz].ls].x,b[b[wz].rs].x);
}
int query(int l,int r,int wz,int x)
{
	if(l==r) return l;int mid=(l+r)>>1;
	if(b[b[wz].ls].x<x) return query(l,mid,b[wz].ls,x);
	return query(mid+1,r,b[wz].rs,x);
}
inline int que(int l,int r){return query(1,V,rt[r],l);}
int main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin>>n;
	V=n+3;for(int i=1;i<=V;i++) g[i].push_back(0);
	for(int i=1;i<=n;i++) cin>>a[i],g[a[i]].push_back(i);
	build(1,V,rt[0]);for(int i=1;i<=n;i++) updata(1,V,rt[i],rt[i-1],a[i],i);
	for(int i=1;i<=V;i++)
	{
		g[i].push_back(n+1);bool ok=0;
		for(int j=0;j<g[i].size()-1;j++)
		{
			int l=g[i][j]+1,r=g[i][j+1]-1;
			if(l<=r&&que(l,r)==i){ok=1;break;}
		}
		if(!ok) return cout<<i,0;
	}
	return 0;
}          

CF1740H

你先别急。

标签:总结,int,void,mid,wz,build,数据结构,mex
From: https://www.cnblogs.com/HaHeHyt/p/17682238.html

相关文章

  • java递归返回树形数据结构
    近期项目有个需求,需要将组织机构数据拼成树型结构返回至前端。我的做法如下方式一、使用递归方式实现privateList<SysDept>getSysDepts(StringdeptId){//1、获取表中所有数据(自行根据实际场景拿到所有表数据)List<SysDept>all=getAllDept();......
  • Linux 命令总结
    Linux文件系统FHS3.0(FilesystemHierarchyStandard)/etc配置文件bin必要命令usr二级目录home家目录var动态数据VFS虚拟文件系统内核层抽象出通用的文件系统接口支持文件、网络、特殊文件系统抽象对象:超级快:文件系统目录项:文件路径索引节点:具体文件文件:进程打开的文件属性分层......
  • uniapp项目实践总结(十)自定义滑动触摸组件
    在APP的日常开放过程中,我们经常可以看到上拉刷新、下拉刷新、左滑、右滑、触底加载等效果,那其中的原理是如何呢,又是如何实现的呢,下面就一探究竟。这篇文章主要是讲述自定义滑动触摸组件的方放,兼容网页H5端、微信小程序端和App端。目录准备工作原理分析组件实现实战......
  • 数据结构代码题-链表
    链表单链表单链表结构体的声明:typedefstructLink{ intdata;//代表数据域 structLink*next;//代表指针域,指向直接后继元素}link;//link为节点名,每个结点都是一个link结构体另一种:typedefstructLNode{ElemTypedata;structLNode*next;}LNode,*Link......
  • 1 C++基础问题总结
    C++基础1C和C++有什么区别?C++是面向对象,C面向过程C++引入new/delete运算符,取代了C中的malloc/free库函数;C++有引用的概念,C没有C++有类的概念,C没有C++有函数重载,C没有2a和&a有什么区别?比如inta[10];int(*p)[10]=&aa是数组名,是数组首元素地址,+1表示地址值加上一......
  • Android总结篇系列:Android Service
    Android总结篇系列:AndroidService Service通常总是称之为“后台服务”,其中“后台”一词是相对于前台而言的,具体是指其本身的运行并不依赖于用户可视的UI界面,因此,从实际业务需求上来理解,Service的适用场景应该具备以下条件:1.并不依赖于用户可视的UI界面(当然,这一条其实也不是绝对的......
  • 2023暑假集训总结-zxy
      在这个暑假集训期间,我度过了充实而有意义的日子,尽管没有很大的进步,也算是有些收获。 在集训中,我阅读完了老师曾经推荐的一本较为简单的数据结构的书,虽然我没有举一反三的能力,但也使我对数据结构有了初步的了解和认识。写题还是照样写不出来,但好像不像以往那样一头雾水了,是......
  • 2023暑假集训总结-lzg
    本人有幸成为程序设计基础暑期集训中的一员,在经历了长达两个月的集训后,我从中收获了很多。    首先是在集训中我学习到了很多知识,在这两个月里,我先是听了一部分ACwing上的课,学到了很多新的算法知识,不过现在掌握的还是相当不熟练。其次为了熟练运用新学到的知识我也在牛......
  • 2023暑假集训总结-wmh
    经过一个多月的集训,我对于基础算法有了系统而全面的认识和学习。在训练前,遇到问题时我只会通过模拟或是靠自己思考来解决题目,经过这次系统性的学习后,我能够通过题目猜出来解决问题所需要的知识点或是大概思路,相较于之前的一窍不通有了很大的提升。在集训中,主要学习了acwing上的算......
  • 2023暑假集训总结-mjh
       在近40天的暑假集训时间内,比赛方面主要是通过牛客上萌新联赛和杭电多校联赛进行练习,偶尔会打cf。日常刷题方面主要是通过洛谷上的官方题单进行练习。首先从日常写题来说,通过洛谷的官方题单,可以对相同类型的题目进行集中训练,对于基础算法:前缀,差分,二分,搜索,快速幂,并查......