Luogu P4137 Rmq Problem/mex

这题可以用莫队做~

先套一波莫队模板,然后我们可以用时间复杂度有时O(n)有时O(1)的的转移操作把这题卡过去qwq……

先复习一遍莫队模板:莫队模板戳这里qwq

我们看到数据范围:$0 \leq a_i \leq 10^9$,很显然我们这个数据不能直接开个数组来统计每个数出现的次数,但我们也知道,取$mex$时有很多数据是没有用的,比如那些大于$n$的数,这些数并不会影响我们的结果,所以我们可以直接排除掉他们。所以计数器数组只需开到$n$就可以了。

然后就是莫队模板啦~

var n,m,i,j,k,x,y,ans:longint;
a,b,l,r,num,an:array[0..200000]of longint;
function check(x1,y1,x2,y2:longint):boolean;
begin
  if (trunc(x1/sqrt(n))>trunc(y1/sqrt(n)))or(trunc(x1/sqrt(n))=trunc(y1/sqrt(n)))and(x2>y2) then
  exit(true)
  else
  exit(false);
end;
procedure sort(ll,rr:longint);
var i,j,x,y,z:longint;
begin
  i:=ll;
  j:=rr;
  x:=l[(ll+rr) div 2];
  z:=r[(ll+rr) div 2];
  repeat
  while check(l[i],x,r[i],z) do
  inc(i);
  while check(x,l[j],z,r[j]) do
  dec(j);
  if not(i>j) then
  begin
    y:=l[i];
    l[i]:=l[j];
    l[j]:=y;
    y:=r[i];
    r[i]:=r[j];
    r[j]:=y;
    y:=num[i];
    num[i]:=num[j];
    num[j]:=y;
    inc(i);
    dec(j);
  end;
  until i>j;
  if ll<j then
  sort(ll,j);
  if i<rr then
  sort(i,rr);
end;
begin
  readln(n,m);
  for i:=1 to n do
  read(a[i]);
  for i:=1 to m do
  begin
    readln(l[i],r[i]);
    num[i]:=i;
  end;
  sort(1,m);
  for i:=l[1] to r[1] do
  if a[i]<=n then//排除大于n的值
  inc(b[a[i]]);
  for i:=0 to n do
  if b[i]=0 then
  begin
    an[num[1]]:=i;//num:答案编号;an:答案
    ans:=i;//ans记录当前最先找到的没有出现过的自然数
    break;
  end;
  x:=l[1];
  y:=r[1];
  for i:=2 to m do
  begin
    while x>l[i] do//左端点向左扩张
    begin
      dec(x);
      if a[x]>n then//值大于n就跳过
      continue;
      inc(b[a[x]]);
      if a[x]=ans then//如果当前扩张到的值就是最小值
      for j:=ans+1 to n do//继续往后搜最小值
      if b[j]=0 then
      begin
        ans:=j;
        break;
      end;
    end;
    while y<r[i] do//右端点向右扩张
    begin
      inc(y);
      if a[y]>n then//值大于n就跳过
      continue;
      inc(b[a[y]]);
      if a[y]=ans then//如果当前扩张到的值就是最小值
      for j:=ans+1 to n do//继续往后搜最小值
      if b[j]=0 then
      begin
        ans:=j;
        break;
      end;
    end;
    while x<l[i] do//左端点向右收缩
    begin
      if a[x]>n then//值大于n就跳过
      begin
        inc(x);
        continue;
      end;
      dec(b[a[x]]);
      if (b[a[x]]=0)and(a[x]<ans) then//如果当前收缩的值剩余个数为0且为最小值
      ans:=a[x];//记为最小值
      inc(x);
    end;
    while y>r[i] do//右端点向左收缩
    begin
      if a[y]>n then//值大于n就跳过
      begin
        dec(y);
        continue;
      end;
      dec(b[a[y]]);
      if (b[a[y]]=0)and(a[y]<ans) then//如果当前收缩的值剩余个数为0且为最小值
      ans:=a[y];//记为最小值
      dec(y);
    end;
    an[num[i]]:=ans;
  end;
  for i:=1 to m do
  writeln(an[i]);
end.

 

转载于:https://www.cnblogs.com/qbwhtc/p/8413383.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值