bzoj3489: A simple rmq problem (主席树)

//==========================

蒟蒻Macaulish:http://www.cnblogs.com/Macaulish/  转载要声明!

//==========================

说好的“因为是OJ上的题,就简单点好了。”呢?

一开始看不懂,不会写。

然后跪了一个晚上决定看云的题解&……似乎是主席树套主席树!吓傻,还开了40000000的数组。然后一交tle……

然后p是不可能玩常数的。

找不到其他做法。

然后找到了神牛dwjshift,好心地提供了题解(http://tieba.baidu.com/p/2947256742#47989538012l )之后还好心地提供了代码!简直业界良心!(dwj必拿AU)

一开始写的是主席树套配对堆(因为要个优先队列嘛)然后发现套个配对堆各种问题(就不说了,其实主要还是我太弱,这个里面很恶心。。。。。)

然后就跪了dwj的代码,用treap实现。

关于题解,正题开始:

主席树套主席树的做法详见帖子中吧主的话

然后发现没有那么必要。

主席树套优先队列详见帖子下面。

“给定一个序列,支持两个操作: 1.给区间[l,r]中塞进或去掉一个数 2.查询某个点的值”

然后类似标记永久化。就可以变成单点修改的线段树套优先队列了

(原来主席树也是可以区间修改区间查询?……只要套个标记永久化)

写的时候有个地方傻叉了,就是主席树修改的时候,如果区间被要修改的区间覆盖时就不用改他儿子,这时候注意不要忘记儿子们=旧的点的儿子们,要是一开始新的点的儿子都是旧的点的不就行了,这是个人风格问题!)

{$inline on}
const
  maxn=200000;
  maxm=12000000;

var
  left,right,size,fix,max2,value:array[0..maxm]of longint;
  root,lson,rson,max:array[0..maxm]of longint;
  time,last,next,num:array[0..maxn]of longint;
  n,m,tot1,tot2,ll,rr:longint;
  flag:boolean;

procedure swap(var x,y:longint);inline;
var
  i:longint;
begin
  i:=x;
  x:=y;
  y:=i;
end;

function mmax(x,y:longint):longint;inline;
begin
  if x<y then exit(y);
  exit(x);
end;

procedure update(x:longint);inline;
begin
  max2[x]:=mmax(max2[left[x]],max2[right[x]]);
  if size[x]>0 then max2[x]:=mmax(max2[x],value[x]);
end;

procedure leftr(var x:longint);inline;
var
  k:longint;
begin
  k:=right[x];
  right[x]:=left[k];
  left[k]:=x;
  max2[k]:=max2[x];
  update(x);
  x:=k;
end;

procedure rightr(var x:longint);inline;
var
  k:longint;
begin
  k:=left[x];
  left[x]:=right[k];
  right[k]:=x;
  max2[k]:=max2[x];
  update(x);
  x:=k;
end;

procedure insert(var x:longint;y:longint);inline;
begin
  if x=0 then begin
    inc(tot2);
    x:=tot2;
    size[x]:=1;
    value[x]:=y;
    fix[x]:=random(maxn);
    max2[x]:=y;
    left[x]:=0;
    right[x]:=0;
    exit;
  end;
  if value[x]=y then begin
    if flag then inc(size[x])
      else dec(size[x]);
    update(x);
  end
  else
    if value[x]<y then begin
      insert(right[x],y);
      update(x);
      if fix[right[x]]>fix[x] then leftr(x);
    end
    else begin
      insert(left[x],y);
      update(x);
      if fix[left[x]]>fix[x] then rightr(x);
    end;
end;

procedure change(x,old,l,r,y:longint;var new:longint);inline;
var
  mid:longint;
begin
  inc(tot1);
  new:=tot1;
  if (ll<=l) and (r<=rr) then begin
    insert(root[x],y);
    max[new]:=max2[root[x]];
    lson[new]:=lson[old];
    rson[new]:=rson[old];
    exit;
  end;
  max[new]:=max2[root[x]];
  mid:=(l+r)>>1;
  if ll>mid then begin
    change(x<<1+1,rson[old],mid+1,r,y,rson[new]);
    lson[new]:=lson[old];
  end
  else
  if rr<=mid then begin
    change(x<<1,lson[old],l,mid,y,lson[new]);
    rson[new]:=rson[old];
  end
  else begin
    change(x<<1,lson[old],l,mid,y,lson[new]);
    change(x<<1+1,rson[old],mid+1,r,y,rson[new]);
  end;
end;

function query(x,l,r,y:longint):longint;inline;
var
  mid:longint;
begin
  //writeln(x);
  if l=r then exit(max[x]);
  mid:=(l+r)>>1;
  if y>mid then exit(mmax(max[x],query(rson[x],mid+1,r,y)))
    else exit(mmax(max[x],query(lson[x],l,mid,y)))
end;

procedure into;inline;
var
  i,j:longint;
begin
  readln(n,m);
  for i:=1 to n do read(num[i]);
  for i:=1 to n do last[i]:=n+1;
  for i:=n downto 1 do begin
    next[i]:=last[num[i]];
    last[num[i]]:=i;
  end;
  for i:=1 to n do
    if last[i]<>n+1 then begin
      ll:=last[i];
      rr:=next[ll]-1;
      flag:=true;
      change(1,time[1],1,n,i,time[1]);
    end;
  for i:=2 to n do begin
    ll:=i-1;
    rr:=next[i-1]-1;
    flag:=false;
    change(1,time[i-1],1,n,num[i-1],time[i]);
    if next[i-1]<>n+1 then begin
      ll:=next[i-1];
      rr:=next[ll]-1;
      flag:=true;
      change(1,time[i],1,n,num[i-1],time[i]);
    end;
  end;
end;

procedure work;inline;
var
  lastans,l,r:longint;
begin
  lastans:=0;
  while m>0 do begin
   // writeln(m,‘===========‘);
    dec(m);
    readln(l,r);
    l:=(l+lastans) mod n+1;
    r:=(r+lastans) mod n+1;
    if l>r then swap(l,r);
    lastans:=query(time[l],1,n,r);
    writeln(lastans);
  end;
end;

begin
  randomize;
  into;
  work;
end.

时间: 2024-10-12 03:57:46

bzoj3489: A simple rmq problem (主席树)的相关文章

bzoj 3489 A simple rmq problem —— 主席树套线段树

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3489 题解:http://www.itdaan.com/blog/2017/11/24/9bc46b690756fe252e17fc3ca90aa01.html 在我挣扎一下午时 Narh 早就A了... 于是看看有何不同,发现 add  和 insert 中必须把 ls[x] = ls[y] , rs[x] = rs[y] 写在前面,而不能是修改 rs 则在那里单写一个 ls[x] =

bzoj3489 A simple rmq problem 可持久化树套树

先预处理出两个个数组pre,next.pre[i]表示上一个与i位置数字相同的位置,若不存在则设为0:next[i]表示下一个与i位置数字相同的位置,若不存在则设为n+1.那么一个满足在区间[L,R]中只出现一次的数字,其pre[i]<L,next[i]>R. 这样我们可以先将pre进行排序,然后将pre可持久化,外层线段树套的是当前数字的位置i,内层线段树套的是next[i].外层线段树的节点总数是nlogn,内层线段树节点总数是nlogn^2.时间复杂度O(nlogn^2). 代码 1 #

【bzoj3489】 A simple rmq problem k-d树

由于某些原因,我先打了一个错误的树套树,后来打起了$k-d$.接着因不明原因在思路上被卡了很久,在今天中午蹲坑时恍然大悟...... 对于一个数字$a_i$,我们可以用一组三维坐标$(i,pre,nxt)$来表示,其中$i$表示该数字下标,$pre$表示在区间$[1,i)$中满足$a[j]=a[i]$的最大$j$,若不存在,则$pre=0$.$nxt$表示在区间$(i,n]$中满足$a[j]=a[i]$的最小$j$,若不存在,则$nxt=n+1$. 接着我们种一棵3-d树去存储这n个点.对于任意

[bzoj3489]A simple rmq problem

本题既不是rmq也不会simple(对我这种蒟蒻而言) 一开始只能想到树套树套树TAT然后看了看数据范围果断滚去膜拜题解. 然后才知道预先排序一下可以弄掉一个log.不过得写可持久化线段树套可持久化线段树.. 然后愉悦的开码了...感人的是竟然不用调...更感人的是交上去直接tle了. 然后从网上找了别人的代码(方法一样)发现同样的数据我要跑6s+..标称只要2s+.. 之后各种卡常还是慢了一倍TAT...最后自己写个max函数就和标程一样快了TAT这几天怎么总是出些奇怪的状况QAQ. 本来故事

【kd-tree】bzoj3489 A simple rmq problem

Orz zyf教给蒟蒻做法 蒟蒻并不会这题正解……(可持久化树套树?...Orz 对于每个点,我们可以求出pre[i],nex[i],那么询问的答案就是:求max (a[i]),其中 i 满足(pre[i]<ql and nex[i]>qr and i∈[ql,qr]) 然后我们以(i,pre[i],nex[i])为坐标……将所有点抽象到三维空间中,每次查询就相当于是一次区域求最值! 这题我的感受: 因为前面做了两道区域求和的……然后思路不由自主又代入到搞[子树最大值]来更新答案……然而忘记了

bzoj 3585: mex &amp;&amp; 3339: Rmq Problem -- 主席树

3585: mex Time Limit: 20 Sec  Memory Limit: 128 MB 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

bzoj 3489 A simple rmq problem - 线段树

Description 因为是OJ上的题,就简单点好了.给出一个长度为n的序列,给出M个询问:在[l,r]之间找到一个在这个区间里只出现过一次的数,并且要求找的这个数尽可能大.如果找不到这样的数,则直接输出0.我会采取一些措施强制在线. Input 第一行为两个整数N,M.M是询问数,N是序列的长度(N<=100000,M<=200000) 第二行为N个整数,描述这个序列{ai},其中所有1<=ai<=N 再下面M行,每行两个整数x,y, 询问区间[l,r]由下列规则产生(OIER

【BZOJ3489】A simple rmq problem kd-tree

[BZOJ3489]A simple rmq problem Description 因为是OJ上的题,就简单点好了.给出一个长度为n的序列,给出M个询问:在[l,r]之间找到一个在这个区间里只出现过一次的数,并且要求找的这个数尽可能大.如果找不到这样的数,则直接输出0.我会采取一些措施强制在线. Input 第一行为两个整数N,M.M是询问数,N是序列的长度(N<=100000,M<=200000) 第二行为N个整数,描述这个序列{ai},其中所有1<=ai<=N 再下面M行,每

【BZOJ】【3489】A simple rmq problem

KD-Tree(乱搞) Orz zyf教给蒟蒻做法 蒟蒻并不会这题正解……(可持久化树套树?...Orz 对于每个点,我们可以求出pre[i],nex[i],那么询问的答案就是:求max (a[i]),其中 i 满足$ ( pre[i]<ql \ and \ nex[i]>qr\ and\ i \in [ql,qr] ) $ 然后我们以(i,pre[i],nex[i])为坐标……将所有点抽象到三维空间中,每次查询就相当于是一次区域求最值! 这题我的感受: 因为前面做了两道区域求和的……然后思路