bzoj 1861 [Zjoi2006]Book 书架

Description

小T有一个很大的书柜。这个书柜的构造有些独特,即书柜里的书是从上至下堆放成一列。她用1到n的正整数给每本书都编了号。小T在看书的时候,每次取出一本书,看完后放回书柜然后再拿下一本。由于这些书太有吸引力了,所以她看完后常常会忘记原来是放在书柜的什么位置。不过小T的记忆力是非常好的,所以每次放书的时候至少能够将那本书放在拿出来时的位置附近,比如说她拿的时候这本书上面有X本书,那么放回去时这本书上面就只可能有X-1、X或X+1本书。 当然也有特殊情况,比如在看书的时候突然电话响了或者有朋友来访。这时候粗心的小T会随手把书放在书柜里所有书的最上面或者最下面,然后转身离开。 久而久之,小T的书柜里的书的顺序就会越来越乱,找到特定的编号的书就变得越来越困难。于是她想请你帮她编写一个图书管理程序,处理她看书时的一些操作,以及回答她的两个提问:(1)编号为X的书在书柜的什么位置;(2)从上到下第i本书的编号是多少。

Input

第一行有两个数n,m,分别表示书的个数以及命令的条数;第二行为n个正整数:第i个数表示初始时从上至下第i个位置放置的书的编号;第三行到m+2行,每行一条命令。命令有5种形式: 1. Top S——表示把编号为S的书房在最上面。 2. Bottom S——表示把编号为S的书房在最下面。 3. Insert S T——T∈{-1,0,1},若编号为S的书上面有X本书,则这条命令表示把这本书放回去后它的上面有X+T本书; 4. Ask S——询问编号为S的书的上面目前有多少本书。 5. Query S——询问从上面数起的第S本书的编号。

Output

对于每一条Ask或Query语句你应该输出一行,一个数,代表询问的答案。

Sample Input

10 10
1 3 2 7 5 8 10 4 9 6
Query 3
Top 5
Ask 6
Bottom 3
Ask 3
Top 6
Insert 4 –1
Query 5
Query 2
Ask 2

Sample Output

2
9
9
7
5
3

数据范围
30%的数据,n,m < = 10000
100%的数据,n,m < = 80000

很明显是到平衡树的题目。再次写了splay不过第一次写splay上面的映射,一开始纠结了书的编号和位置两个关键字的实现上。。。太弱了。。。
 
说说想法。一开始为什么写不出来呢?因为纠结了改变了书的位置以后以编号为关键字怎么查找?想起以前总结的一句话:splay操作只是指针的改变,而每个节点的储存内容是不变的。
顿时通了。本题没有修改编号,那么直接记录一开始编号为k的书对应的存储单元,那么这个存储单元在后面的各种单旋双旋删除添加的操作中所对应的书籍是不会变的。这样就可以两个数组建立书籍到节点的双射,需要的时候从树中询问出来或者是询问树中节点都可以O(1)做到了。(其实更改编号的话也很简单,反正建立了双射就可以做到O(1)更改映射关系)。
 
然后就是实现了。还是犯了比较nc的错误。。。处理儿子个数的时候没有加上本身什么的、、还有就是查找第k大的数的时候没有处理好0和n的询问导致超界。。。反正各种nc。。。
 
还有后来一直调竟然是因为把字符读到整型上了。。。无奈了。。。
 
感觉加没加inline速度没差。。。
 
AC CODE
 
{$inline on}
program zjoi_2006_day2_book;
var fa,pi,po,son:array[0..80000] of longint;
    c:array[0..80000,0..1] of longint;
    root,n,m:longint;
//=====================================================================
procedure rotate(var root:longint; x:longint); inline;
var y,z,p,q:longint;
begin
  y:=fa[x]; z:=fa[y];
  if c[y,0]=x then p:=0 else p:=1;
  q:=p xor 1;
  if y=root then root:=x else
    if c[z,0]=y then c[z,0]:=x else c[z,1]:=x;
  if c[x,q]>0 then fa[c[x,q]]:=y;
  fa[x]:=z; fa[y]:=x;
  c[y,p]:=c[x,q]; c[x,q]:=y;
  son[y]:=son[c[y,0]]+son[c[y,1]]+1;
  son[x]:=son[y]+son[c[x,p]]+1;    //一开始这里没加1
end;
//=====================================================================
procedure splay(var root:longint; x:longint); inline;
var y,z:longint;
begin
  while x<>root do
  begin
    y:=fa[x]; z:=fa[y];
    if y<>root then
      if (c[y,0]=x) xor (c[z,0]=y) then
        rotate(root,x) else rotate(root,y);
    rotate(root,x);
  end;
end; //经过多次实践,表示敲出splay和rotate的代码可以选择跳过调试了、、如果没有更新附加域的话。。
//=====================================================================
procedure init;
var i,x:longint;
begin
  readln(n,m);
  for i:=1 to n do
  begin
    read(x); pi[x]:=i;
    po[i]:=x; son[i]:=n-i+1;
  end; readln;    //这里没有readln的错误还是第一次碰到。。。
  for i:=1 to n-1 do c[i,1]:=i+1;
  for i:=1 to n do fa[i]:=i-1;
  root:=1; splay(root,n);
end;
//=====================================================================
function delete(x:longint):longint; inline;
var left,right:longint;
begin
  splay(root,x);
  left:=c[root,0]; right:=c[root,1];
  while c[left,1]>0 do left:=c[left,1];
  while c[right,0]>0 do right:=c[right,0];
  if left>0 then splay(root,left);
  if right>0 then
    if left>0 then splay(c[root,1],right) else
      splay(root,right);
  if right=0 then
  begin delete:=n;
    c[left,1]:=0;
    if left>0 then dec(son[left]);
  end else
  begin
    if left>0 then
      delete:=son[left]-son[right]+1 else
        delete:=1;
    c[right,0]:=0; dec(son[right]);
    if left>0 then dec(son[left]);
  end; fa[x]:=0; son[x]:=1;
end;
//=====================================================================
function find(x:longint):longint; inline;
var i:longint;
begin
  i:=root;
  while i<>0 do
  begin
    if son[c[i,0]]+1=x then exit(i);
    if son[c[i,0]]>=x then i:=c[i,0] else
    begin
      dec(x,son[c[i,0]]+1); i:=c[i,1];
    end;
  end; exit(i);
end;
//=====================================================================
procedure ins(x,y:longint); inline;
var left,right:longint;
begin
  left:=find(y-1); right:=find(y);
  if left>0 then splay(root,left);
  if right>0 then
    if left>0 then splay(c[root,1],right) else
      splay(root,right);
  if right=0 then
  begin
    fa[x]:=left; c[left,1]:=x; inc(son[left]);
  end else
  begin
    inc(son[right]); fa[x]:=right; c[right,0]:=x;
    if left>0 then inc(son[left]);
  end;
end;
//=====================================================================
procedure main;
var ch,ch1:char;
    i,s,t,pos:longint;
begin
  for i:=1 to m do
  begin
    repeat
      ch1:=ch; read(ch);
    until ch=' '; ch:=ch1; //如果init里面没有readln这里就会直接跳掉。读不到指令。然后就215了。。。
    if ch='p' then
    begin readln(s);
      pos:=delete(pi[s]); ins(pi[s],1);
    end else
    if ch='m' then
    begin readln(s);
      pos:=delete(pi[s]); ins(pi[s],n);
    end else
    if ch='t' then
    begin readln(s,t);
      if t=0 then continue;
      pos:=delete(pi[s]); ins(pi[s],pos+t);
    end else
    if ch='k' then
    begin readln(s);
      splay(root,pi[s]);
      writeln(son[c[root,0]]);
    end else
    begin readln(s);
      pos:=find(s); splay(root,pos);
      writeln(po[pos]);
    end;
  end;
end;
//=====================================================================
begin
  assign(input,'book.in'); reset(input);
  assign(output,'book.out'); rewrite(output);
  init;
  main;
  close(input); close(output);
end.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值