Rmq Problem bzoj-3339||mex bzoj-3585

题目大意:给定一个长度为n的数列a,多次讯问区间l,r中最小的不属于集合{$A_l,A_{l+1}...A_r$}的非负整数。

注释:n,q$\le$200,000 ; 0$\le A_i \le$200,000 ; $A_i$均为非负整数,1<=l<=r<=n,l和r均为正整数。

想法:网上很多其他的算法(suika:离线+莫队,WinnieChen:在线权值线段树),我们来聊一聊离线加线段树。

  首先,我们将询问离线,按左端点为第一关键字排序。

  紧接着,预处理一点东西:暴力求出1~i之间的答案,以及每一个数位置pos的数a[pos]右边第一个等于a[pos]的数,它的位置我们记为nxt[pos]。

  然后,我们从左往右扫,对于当前数a[l],对于l+1到nxt[l]-1之间的数都需要进行区间赋值乘a[l],这个操作我们用线段树完成。如果当前节点还是一个被查询区间的左端点的话,我们直接输出右端点对应的mex即可。

最后,附上丑陋的代码... ...

#include <iostream>
#include <cstdio>
#include <algorithm>
#define lson pos<<1
#define rson pos<<1|1
#define inf 0x7fffffff
using namespace std;
int n,m,k=0;
int a[200005],sg[200005],ans[200005],next[200005],last[200005];
int ls[600005],rs[600005],mn[600005];
bool mark[200001];
struct data{int l,r,id;}q[200005];
bool cmp(data a,data b)
{return a.l<b.l;}
void build(int l,int r,int pos)
{
ls[pos]=l;
rs[pos]=r;
mn[pos]=inf;
if(l==r)
{
mn[pos]=sg[l];
return;
}
int mid=(l+r)>>1;
build(l,mid,lson);build(mid+1,r,rson);
}
void pushdown(int pos)
{
int l=ls[pos],r=rs[pos];
if(l==r)return;
mn[lson]=min(mn[pos],mn[lson]);
mn[rson]=min(mn[pos],mn[rson]);
}
int ask(int pos,int x)
{
if(mn[pos]!=inf)pushdown(pos);
int l=ls[pos],r=rs[pos];
if(l==r)return mn[pos];
int mid=(l+r)>>1;
if(x<=mid)return ask(lson,x);
return ask(rson,x);
}
void update(int pos,int x,int y,int val)
{
if(mn[pos]!=inf)pushdown(pos);
int l=ls[pos],r=rs[pos];
if(l==x&&y==r){mn[pos]=min(mn[pos],val);return;}
int mid=(l+r)>>1;
if(y<=mid)update(lson,x,y,val);
else if(x>mid)update(rson,x,y,val);
else
{
update(lson,x,mid,val);
update(rson,mid+1,y,val);
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++)
{
mark[a[i]]=1;
if(a[i]==k)
while(mark[k])k++;
sg[i]=k;
}
build(1,n,1);
for(int i=n;i>0;i--)
next[i]=last[a[i]],last[a[i]]=i;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&q[i].l,&q[i].r);
q[i].id=i;
}
sort(q+1,q+m+1,cmp);
int now=1;
for(int i=1;i<=m;i++)
{
while(now<q[i].l)
{
if(!next[now])next[now]=n+1;
update(1,now,next[now]-1,a[now]);
now++;
}
ans[q[i].id]=ask(1,q[i].r);
}
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
return 0;
}

小结:对着hzwer学长写的动态开点,感觉爆炸。注意pushdown的时候要记得删除之前的标记。

最新文章

  1. reduce方法
  2. 01-03-02-2【Nhibernate (版本3.3.1.4000) 出入江湖】CRUP操作-Save方法的一些问题
  3. Java 中UDP原理机制及实现方式介绍(建议阅读者阅读前了解下Java的基础知识,一方便理解)
  4. git 简单教程更新
  5. hdu 1299 Diophantus of Alexandria(数学题)
  6. Spring IOC(三)依赖注入
  7. 财务CLOUD成本核算
  8. word20161231
  9. [CF1093E]Intersection of Permutations
  10. 在EXT框架中,使用JS文件设置UEditor文本框,出现新增内容很多,页面变型,不出现滚动条,导致无法进行操作。
  11. ABAP 内表访问表达式的性能
  12. mysql分组统计以及全部统计union all使用
  13. 创建新用户,及用新用户名和密码登录--------------DCL
  14. Maven私服仓库类型
  15. jenkins自定义安装目录
  16. [转载]使用PHP模拟HTTP认证
  17. SQL中distinct的用法(转载)
  18. TC 配置插件
  19. 486. Predict the Winner
  20. Request.QueryString 的用法

热门文章

  1. P2533 [AHOI2012]信号塔
  2. selenium3 + python - action_chains源码分析
  3. Python基础类型(二) str 字符串
  4. Codeforces 766E
  5. Codeforces 612D 前缀和处理区间问题
  6. HDU4340 Capturing a country DP
  7. 【Leetcode 3】Longest Substring Without Repeating Characters0
  8. Task.Run 和 Task.Factory.StartNew
  9. [ ZJOI 2006 ] Mahjong
  10. 将MongoDB服务器设置成Windows启动服务(win10)