bzoj 3585: mex

Description

  有一个长度为n的数组{a1,a2,...,an}。m次询问,每次询问一个区间内最小没有出现过的自然数。

Input

  第一行n,m。
  第二行为n个数。
  从第三行开始,每行一个询问l,r。

Output

  一行一个数,表示每个询问的答案。

Sample Input

5 5
2 1 0 2 1
3 3
2 3
2 4
1 2
3 5

Sample Output

1
2
3
0
3

HINT

数据规模和约定

  对于100%的数据:

  1<=n,m<=200000

  0<=ai<=109

  1<=l<=r<=n


  对于30%的数据:


  1<=n,m<=1000


和3339一样

------------以下摘自3339------------

我们先暴力弄出以1为开头到位置x的mex[x],可以体会到mex[]是单调的。

然后就可以乱搞了。

对于询问左端点排序,然后当左端点=当前mex左端点的时候直接输出

如果不等于,那么我们记录当前数下一个出现位置next[i]

那么可以把i+1到next[i]-1都更新一遍,因为mex是单调的,所以反过来更新,从next[i]-1到i+1,一旦不能更新就break

这样就可以得出所有的答案了

--------------------------------------------

然后这题只是多个离散化操作。给mex赋初值要稍微处理一下就好

#include<cstdio>
#include<algorithm>
using namespace std;
int a[200001],v[200001],last[200001],mex[200001],next[200001];
int ans[200001];
int n;
struct getask
{
     int l,r;
     int t;
}ask[200001];
struct num
{
     int x,t;
}b[200001];
inline bool cmp(num x,num y)
{
     if(x.x<y.x)
          return true;
     return false;
}
inline bool cmp1(getask x,getask y)
{
     if(x.l<y.l)
          return true;
     return false;
}
inline bool cmp2(getask x,getask y)
{
     if(x.t<y.t)
          return true;
     return false;
}
inline void change(int x)
{
     int i;
     int ed=next[x];
     if(ed==0)
          ed=n+1;
     mex[x]=0;
     for(i=ed-1;i>x;i--)     //这里倒着更新太神。。 
     {
          if(mex[i]>a[x])
               mex[i]=a[x];
          else
               break;
     }
}
int f[200001];
int fx[200001];
int main()
{
     int q;
     scanf("%d%d",&n,&q);
     int i,j;
     for(i=1;i<=n;i++)
     {
          scanf("%d",&a[i]);
          b[i].x=a[i];
          b[i].t=i;
     }
     sort(b+1,b+1+n,cmp);
     int tot=1;
     f[tot]=0;
     fx[0]=tot;
     for(i=1;i<=n;i++)
     {
          if(b[i].x!=f[tot])
          {
               tot++;
               f[tot]=b[i].x;
          }
		  fx[b[i].t]=tot;
     }
     int now=1;
     for(i=1;i<=n;i++)
     {
     	  int d;
     	  v[fx[i]]=1;
          while(v[now]!=0&&f[now+1]==f[now]+1)
               now++;
          if(v[now]!=0&&f[now+1]!=f[now]+1)
               d=f[now]+1;
          else
               d=f[now];
          mex[i]=d;
     }
     for(i=1;i<=n;i++)
     {
          next[last[fx[i]]]=i;
          next[i]=n+1;
          last[fx[i]]=i;
     }
     for(i=1;i<=q;i++)
     {
          scanf("%d%d",&ask[i].l,&ask[i].r);
          ask[i].t=i;
     }
     sort(ask+1,ask+1+q,cmp1);
     int la=1;
     for(i=1;i<=q;i++)
	 {
          if(ask[i].l==la)
               ans[ask[i].t]=mex[ask[i].r];
          else
		  {
               for(j=la;j<ask[i].l;j++)
                    change(j);
               ans[ask[i].t]=mex[ask[i].r];
          }
          la=ask[i].l;
     }
     for(i=1;i<=q;i++)
          printf("%d\n",ans[i]);
     return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值