BZOJ3585 & 洛谷4137 mex 题解(卡掉暴力莫队+主席树)

题目:BZOJ3585/lugou4137.
题目大意:给定一个长度为 n n n的数列以及 m m m个询问,每个询问查询区间 [ l , r ] [l,r] [l,r]中最小的没有出现的自然数.
1 ≤ n , m ≤ 2 ∗ 1 0 5 1\leq n,m\leq 2*10^5 1n,m2105.

接下来我们称 [ l , r ] [l,r] [l,r]内最小的没有出现的自然数为 m e x [ l , r ] mex[l,r] mex[l,r].

首先容易发现 m e x [ l , r ] ≤ r − l + 1 mex[l,r]\leq r-l+1 mex[l,r]rl+1,因为 [ l , r ] [l,r] [l,r]中的数最坏情况下只会把 [ 0 , r − l ] [0,r-l] [0,rl]填满,所以本题并不需要离散化.

现在考虑一个莫队的做法,用一个计数数组记录当前区间 [ l , r ] [l,r] [l,r]中每个权值的出现次数和 m e x [ l , r ] mex[l,r] mex[l,r].然后删除一个权值的时候,若这个点空了,则尝试更新当前答案;插入一个权值的时候,若刚好是当前答案呗搞掉了,就大力往后查找第一个空的位置.

这个做法怎么看都是假的,然而却有人说这样做是可以证明每次插入操作的单调性什么的来保证均摊 O ( 1 ) O(1) O(1)

现在我们来hack一下这个做法,容易想到一个简单的构造数据的方法:考虑生成一个序列 0 , 1 , 2 , . . . , n − 1 0,1,2,...,n-1 0,1,2,...,n1,然后每次询问的端点按照 1 , 2 , 1 , 2 , 1 , 2 , . . . , 1 , 2 1,2,1,2,1,2,...,1,2 1,2,1,2,1,2,...,1,2循环,右端点直接递增生成,譬如 1 , 2 , 3 , 4 , 5 , 6 , . . . , n 1,2,3,4,5,6,...,n 1,2,3,4,5,6,...,n即可.

然后我们就开心愉快地把这道题的朴素莫队做法卡掉了,这样的数据让朴素莫队跑理论上可以把它卡到 O ( n 2 ) O(n^2) O(n2)

当然这道题的莫队可以通过树状数组+倍增做到 O ( n n log ⁡ n ) O(n\sqrt{n}\log n) O(nn logn)或者用分块优化或直接回滚莫队做到 O ( n n ) O(n\sqrt{n}) O(nn ).

那么现在我们不考虑莫队,直接用主席树是否可做呢?

考虑直接记录每一个前缀的权值,然后发现这个转化为权值后的信息并不满足区间减法性质…

换一个思路,用主席树记录每个前缀的每一个权值的最后出现的位置(即下标),然后每次查询 m e x [ l , r ] mex[l,r] mex[l,r]就可以通过第 r r r棵主席树上二分做.具体就是二分权值 m i d mid mid,然后看 [ l , m i d ] [l,mid] [l,mid]中记录的下标的最小值是否小于 l l l,若小于 l l l说明答案在 [ l , m i d ] [l,mid] [l,mid]上,否则说明答案在 [ m i d + 1 , r ] [mid+1,r] [mid+1,r]上.

时空复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn).

代码如下:

#include<bits/stdc++.h>
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=200000,C=30;

int n,m,a[N+9];
struct tree{
  int min,s[2];
}tr[N*C+9];
int cn,rot[N+9];

void Build(int &k){tr[k=cn=0]=tree();}
void Pushup(int k){tr[k].min=min(tr[tr[k].s[0]].min,tr[tr[k].s[1]].min);}

void Change(int p,int v,int l,int r,int hk,int &k){
  tr[k=++cn]=tr[hk];
  if (l==r){tr[k].min=v;return;}
  int mid=l+r>>1;
  p<=mid?Change(p,v,l,mid,tr[hk].s[0],tr[k].s[0]):Change(p,v,mid+1,r,tr[hk].s[1],tr[k].s[1]);
  Pushup(k);
}

int Query_mex(int p,int l,int r,int k){
  if (l==r) return l;
  int mid=l+r>>1;
  return p>=tr[tr[k].s[0]].min?Query_mex(p,l,mid,tr[k].s[0]):Query_mex(p,mid+1,r,tr[k].s[1]);
}

Abigail into(){
  scanf("%d%d",&n,&m);
  for (int i=1;i<=n;++i){
    scanf("%d",&a[i]);
  }
}

Abigail work(){
  Build(rot[0]);
  for (int i=1;i<=n;++i)
    if (a[i]>n) rot[i]=rot[i-1];
	else Change(a[i],i,0,n,rot[i-1],rot[i]);
}

Abigail getans(){
  int l,r;
  for (int i=1;i<=m;++i){
  	scanf("%d%d",&l,&r);
  	printf("%d\n",Query_mex(l-1,0,n,rot[r]));
  }
}

int main(){
  into();
  work();
  getans();
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值