bzoj 3339 线段树离线处理

题意:给定一个n个数的序列,多次询问,每次询问区间[l,r]的mex

直接暴力显然不可

区间[l,r]和区间[l',r']mex的情况:

(1)[l,r]和[l',r']的mex值不同:[l,r]的mex值在[l',r']中出现 或 原本在[l,r]中存在而不在[l',r']中存在从而成为[l',r']的mex值 (反之同理)

(2)[l,r]和[l',r']的mex值相同:区间内出现的元素相同或只有比mex值大的元素不同

类似莫队的思想,如果已知[l,r]的mex值,那么[l+1,r]的mex值会出现以下情况:

(1)与[l,r]的mex值相同

(2)[l+1,r]中没有出现元素a[l]且a[l]成为了[l',r']的mex值

也就是说,只需要找到a[l]下一次出现的位置p,并把[l,p-1]中所有sg值比a[l]大的全都替换成a[l]即可

如果直接修改会T,那么就可以利用线段树区间修改即可

那么我们就需要离线,按查询的左端点为第一关键字、右端点为第二关键字排序

线段树中,真正有用的只有叶子结点,建一棵完整的线段树只是为了存一下lazy...mex本身是不资磁合并的...

uses math;
type
        rec=record
            mex:longint;
end;

type
        rec2=record
             l,r,id:longint;
end;

var
        n,m,tt          :longint;
        i               :longint;
        vis             :array[0..200010] of boolean;
        sg,a,last,next  :array[0..200010] of longint;
        q               :array[0..200010] of rec2;
        ans             :array[0..200010] of longint;
        t               :array[0..600010] of rec;

procedure sort(l,r:longint);
var
        i,j,x,y:longint;
        z:rec2;
begin
   i:=l; j:=r;
   x:=q[(l+r)>>1].l; y:=q[(l+r)>>1].r;
   while i<=j do
   begin
      while (q[i].l<x) or (q[i].l=x) and (q[i].r<y) do inc(i);
      while (q[j].l>x) or (q[j].l=x) and (q[j].r>y) do dec(j);
      if i<=j then
      begin
         z:=q[i]; q[i]:=q[j]; q[j]:=z;
         inc(i); dec(j);
      end;
   end;
   if i<r then sort(i,r);
   if j>l then sort(l,j);
end;

procedure build(x,l,r:longint);
var
        mid:longint;
begin
   if l=r then
   begin
      t[x].mex:=sg[l]; exit;
   end;
   mid:=(l+r)>>1;
   build(x<<1,l,mid);
   build((x<<1)+1,mid+1,r);
   t[x].mex:=maxlongint;
end;

procedure update(x,l,r:longint);
begin
   if l=r then exit;
   t[x<<1].mex:=min(t[x<<1].mex,t[x].mex);
   t[(x<<1)+1].mex:=min(t[(x<<1)+1].mex,t[x].mex);
   t[x].mex:=maxlongint;
end;

function find(x,l,r,y:longint):longint;
var
        mid:longint;
begin
   if t[x].mex<>maxlongint then update(x,l,r);
   if (l=r) and (l=y) then exit(t[x].mex);
   mid:=(l+r)>>1;
   if y<=mid then exit(find(x<<1,l,mid,y)) else
     exit(find((x<<1)+1,mid+1,r,y));
end;

procedure change(x,l,r,x1,x2,y:longint);
var
        mid:longint;
begin
   if (l=x1) and (r=x2) then
   begin
      t[x].mex:=min(t[x].mex,y); exit;
   end;
   if t[x].mex<>maxlongint then update(x,l,r);
   mid:=(l+r)>>1;
   if x2<=mid then change(x<<1,l,mid,x1,x2,y) else
     if x1>mid then change((x<<1)+1,mid+1,r,x1,x2,y) else
     begin
        change(x<<1,l,mid,x1,mid,y);
        change((x<<1)+1,mid+1,r,mid+1,x2,y);
     end; 
end;

begin
   read(n,m);
   for i:=1 to n do
   begin
      read(a[i]);
      if last[a[i]]<>0 then next[last[a[i]]]:=i;
      last[a[i]]:=i;
   end;
   tt:=0;
   for i:=1 to n do
   begin
      vis[a[i]]:=true;
      while vis[tt] do inc(tt);
      sg[i]:=tt;
   end;
   build(1,1,n);
   //
   for i:=1 to m do read(q[i].l,q[i].r);
   for i:=1 to m do q[i].id:=i;
   sort(1,m);
   tt:=1;
   for i:=1 to m do
   begin
      while q[i].l>tt do
      begin
         if next[tt]=0 then next[tt]:=n+1;
         change(1,1,n,tt,next[tt]-1,a[tt]);
         inc(tt);
      end;
      ans[q[i].id]:=find(1,1,n,q[i].r);
   end;
   for i:=1 to m do writeln(ans[i]);
end.
——by Eirlys



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值