题目描述
生日快乐 【任务描述】 今天是栋栋的生日,他邀请了 N 个好友参加 Party。朋友们都知道,栋栋最 喜欢吃果冻。因此, 每个朋友带来的生日礼物全是一包果冻.。 在每个朋友送他一包果冻的同时, 栋栋还要这个朋友送他一个幸运号码 L(1≤ L ≤ N) 。然后栋栋会先把这包果冻放在一旁,并且把之前的所有果冻包按照果冻的数量从小到大排序(如果果冻数量相等,先后顺序任意)。接着,栋栋再把 当前这包果冻插入到有序的果冻包队列中,使得这个队列仍然有序(如果存在其他的果冻包与该果冻包数量相等,则把该果冻包放在它们的前面)。完成这个操作后,栋栋就会进行如下操作 : ·如果这个朋友是男生,栋栋会从他送的包的后一个包开始向后数 L 个 (该朋友的幸运号码),从那个包里取出一个果冻,吃掉。 ·如果这个朋友是女生,栋栋会从她送的包的前一个包开始向前数 L 个 (该朋友的幸运号码),从那个包里取出一个果冻,吃掉。 栋栋实在是太粗心了,以至于他收完所有的礼物后,都不知道吃过哪些朋友 的果冻,现在,他希望你帮他一下,当他每吃一个果冻后马上告诉他可能吃的是 谁送的(由于排序不是确定的,所以栋栋只要你给他一种可能的答案就行了)。 这是一个交互式的题目,你必须调用库函数来完成所有操作而不能访问任何 文件。 对于 Pascal 的用户: 1.使用库 你必须引用 happybirthday_lib_p 单元,并在源代码的第一行加入 uses happybirthday_lib_p; 2.库函数 init: 定义:procedure init; 调用:init; 说明:这个函数在你的程序中必须调用且仅调用一次,它的功能是初始化库。 getpresent: 定义:function getpresent(var count: longint; var luckynumber: longint; var isboy: boolean): boolean; 调用:isend := getpresent(count, luckynumber, isboy); 其中 count 和 luckynumber 是两个长整型的变量,isboy 和 isend 是两个布尔型变 说明:这个函数用来获得下一个朋友的礼物,count 表示礼物包中果冻的个数, lukcynumber 是这个朋友给栋栋的幸运数字 L, isboy 用来标志这个朋友的性 别,如果是 true,表示是男生,否则表示是女生。 如果后面还有朋友要送给栋栋礼物,函数返回 true,否则返回 false,此时 你的程序应当结束运行。 tell: 定义:procedure tell(const friendid: longint); 调用:tell(friendid); 其中 friendid 可以是任何可以计算出长整型结果的表达式。 说明:这个函数用来告诉栋栋刚才吃的是第几个朋友送的果冻。如果这个朋友不 存在,或者该朋友送的果冻包内已无果冻,则 friendid 应为-1。 每调用一次 getpresent,如果返回值是 true,必须调用一次 tell。 blockmsg: 定义:procedure blockmsg; 调用:blockmsg; 说明:这是一个为了方便调试而附加的函数。因为写文件的速度较慢,利用此函 数可以屏蔽库写调用的相关信息,以便于进行速度测试。在实际测试过程中,这 个函数将被定义为一个空函数,你的程序中可以有任意次数对该函数的调用,你 不必担心因这个函数而影响程序的正确性和速度。 对于 C++的用户: 1.使用库 你必须使用 happybirthday_lib_c 库,并在源代码中加入 #include “happybirthday_lib_c.h” 并在工程加入 happybirthday_lib_c.o。 2.库函数 init: 定义:void init(); 调用:init(); 说明:这个函数在你的程序中必须调用且仅调用一次,它的功能是初始化库。 getpresent: 定义:bool getpresent(long &count, long &luckynumber, bool &isboy); 调用:isend = getpresent(count, luckynumber, isboy); 其中 count 和 luckynumber 是两个长整型的变量,isboy 和 isend 是两个布尔型变 量。 说明:这个函数用来获得下一个朋友的礼物,count 表示礼物包中果冻的个数, lukcynumber 是这个朋友给栋栋的幸运数字 L, isboy 用来标志这个朋友的性 别,如果是 true,表示是男生,否则表示是女生。 如果后面还有朋友要送给栋栋礼物,函数返回 true,否则返回 false,此时 你的程序应当结束运行。 tell: 定义:void tell(const long friendid); 调用:tell(friendid); 其中 friendid 可以是任何可以计算出长整型结果的表达式。 说明:这个函数用来告诉栋栋刚才吃的是第几个朋友送的果冻。如果这个朋友不 存在,或者该朋友送的果冻包内已无果冻,则 friendid 应为-1。 每调用一次 getpresent,如果返回值是 true,必须调用一次 tell。 blockmsg: 定义:void blockmsg(); 调用:blockmsg(); 说明:这是一个为了方便调试而附加的函数。因为写文件的速度较慢,利用此函 数可以屏蔽库写调用的相关信息,以便于进行速度测试。在实际测试过程中,这 个函数将被定义为一个空函数,你的程序中可以有任意次数对该函数的调用,你 不必担心因这个函数而影响程序的正确性和速度。 【如何测试自己的程序】 为了测试自己的程序,你应该在程序所在的目录中建立一个名为 happybirthday.in 的文件。文件格式如下: 文件的第一行为一个整数 n,表示送给栋栋礼物的朋友个数。 接下来 n 行,每行三个整数,总第 i+1 行的三个整数分别表示第 i 个朋友送 的果冻包内的果冻个数、幸运数字以及这个朋友的性别,如果是男生,性别用数 字 1 表示,如果是女生,性别用 0 表示。 如果你的目录下有这个文件,库将从这个文件里面读入礼物的信息,并把结 果输出到 happybirthday.out 中,里面可能有如下的信息: ·call init():调用函数 init ·Error: recall init():已经调用过 init,又重复调用 ·Error: not init:调用其他函数前没有调用 init ·getpresent: **:调用 getpresent,后面的**是函数的返回信息 ·god bless:调用 getpresent,所有的礼物都已经处理完,如果你的一切调用都 正确,这句话应该是 happybirthday.out 的最后一句 ·Error: no present left:调用 getpresent 返回 false 后,又调用了 getpresent ·Error: not told yet:两次 getpresent 之间没有调用 tell ·tell: **:调用 tell,后面的**是函数的参数信息 ·Error: ear overflow:连续调用 tell,在没调用 getpresent 的情况下调用 tell 或者 getpresent 返回 false 后调用 tell 在测试自己的程序的时候,你必须保证你的输入文件是按照给定格式的,否 则可能会出现运行异常。 【数据规模和约定】 对于所有的数据, 我们保证: ≤ n ≤ 500000, ≤ count ≤ 108, 1 ≤ luckynumber≤ n.在测试时,你的数据也应该满足我们的数据范围,否则有可能运行异常。 【评分方法】 如果你的程序出现如下情况,该测试点 0 分: 访问了任何文件(包括临时文件) ; 非法调用库函数; 让测试库异常退出; 答案错误。 否则该测试点得满分。 【样例】 happybirthday.in 3 32 1 1 1 1 1 23 1 0 Pascal 源程序 uses happybirthday_p; var count, luckynumber: longint; isboy: boolean; begin init; getpresent(count, luckynumber, isboy); tell(-1); getpresent(count, luckynumber, isboy); tell(1); getpresent(count, luckynumber, isboy); tell(2); getpresent(count, luckynumber, isboy); end. C++源程序 #include "happybirthday_cpp.h" long count, luckynumber; bool isboy; int main() { init(); getpresent(count, luckynumber, tell(-1); getpresent(count, luckynumber, tell(1); getpresent(count, luckynumber, tell(2); getpresent(count, luckynumber, return 0; } isboy); isboy); isboy); isboy); 运行后,库将生成如下信息在 happybirthday.out 中 call init() getpresent: count=32,luckynumber=1,isboy=true Return true tell: -1 getpresent: count=1,luckynumber=1,isboy=true Return true tell: 1 getpresent: count=23,luckynumber=1,isboy=false Return true tell: 2 getpresent: Return false god bless
splay
1 (* 2 *Problem: NOI2006 生日快乐 3 *Author : Chen Yang 4 *Time : 2012.5.31 2:00 pm 5 *State : Solved 6 *Memo : splay 7 *) 8 program happybirthday; 9 uses happybirthday_lib_p; 10 var 11 n,luky,cnt,root,t,i:longint; 12 isboy:boolean; 13 s,l,r,father,v,num:array[0..1000000] of longint; 14 //=================== 15 procedure l_rotate(x:longint); inline; 16 var 17 y,z:longint; 18 begin 19 y:=r[x]; z:=father[x]; 20 if x=l[z] then l[z]:=y else 21 if x=r[z] then r[z]:=y; 22 father[y]:=z; father[l[y]]:=x; father[x]:=y; 23 r[x]:=l[y]; l[y]:=x; father[0]:=0; 24 s[x]:=s[l[x]]+s[r[x]]+1; s[y]:=s[l[y]]+s[r[y]]+1; 25 end; 26 //=================== 27 procedure r_rotate(x:longint); inline; 28 var 29 y,z:longint; 30 begin 31 y:=l[x]; z:=father[x]; 32 if x=l[z] then l[z]:=y else 33 if x=r[z] then r[z]:=y; 34 father[y]:=z; father[r[y]]:=x; father[x]:=y; 35 l[x]:=r[y]; r[y]:=x; father[0]:=0; 36 s[x]:=s[l[x]]+s[r[x]]+1; s[y]:=s[l[y]]+s[r[y]]+1; 37 end; 38 //=================== 39 procedure splay(x,p:longint); 40 var 41 y,z:longint; 42 begin 43 z:=father[p]; 44 while father[x]<>z do 45 begin 46 y:=father[x]; 47 if x=l[y] then r_rotate(y) else l_rotate(y); 48 end; 49 if p=root then root:=x; 50 end; 51 //=================== 52 function insert(var k:longint; t,x,fa:longint):longint; 53 begin 54 if k=0 then 55 begin 56 inc(n); 57 v[n]:=t; s[n]:=1; father[n]:=fa; num[n]:=x; 58 l[n]:=0; r[n]:=0; 59 k:=n; exit(n); 60 end; 61 inc(s[k]); 62 if t<=v[k] then exit(insert(l[k],t,x,k)) else exit(insert(r[k],t,x,k)); 63 end; 64 //=================== 65 procedure delete(x:longint); 66 var 67 i:longint; 68 begin 69 splay(x,root); 70 i:=l[root]; 71 if i>0 then 72 begin 73 while r[i]>0 do i:=r[i]; 74 splay(i,l[root]); 75 r[i]:=r[root]; 76 father[r[i]]:=i; 77 s[i]:=s[root]-1; 78 root:=i; 79 end else root:=r[root]; 80 father[root]:=0; 81 end; 82 //=================== 83 function find(x,k:longint):longint; 84 begin 85 if x=0 then exit(-1); 86 if s[l[x]]=k-1 then exit(x); 87 if s[l[x]]>k-1 then exit(find(l[x],k)); 88 exit(find(r[x],k-s[l[x]]-1)); 89 end; 90 //=================== 91 begin 92 init; root:=0; i:=0; 93 blockmsg; 94 while getpresent(cnt,luky,isboy) do 95 begin 96 inc(i); 97 splay(insert(root,cnt,i,0),root); 98 if isboy then t:=find(r[root],luky) 99 else t:=find(l[root],s[l[root]]-luky+1); 100 if t>0 then 101 begin 102 delete(t); dec(v[t]); 103 if v[t]>0 then insert(root,v[t],num[t],0); 104 end; 105 if t>0 then tell(num[t]) else tell(-1); 106 end; 107 end.
treap变成的排序二叉树
1 (* 2 *Problem: NOI2006 生日快乐 3 *Author : Chen Yang 4 *Time : 2012.5.31 2:00 pm 5 *State : Solved 6 *Memo : treap(被卡,于是)取消左右旋——排序二叉树 7 *) 8 program happybirthday; 9 uses happybirthday_lib_p; 10 const maxn=1000100; 11 var 12 n,m,luck,cnt,root,rak,t:longint; 13 isboy:boolean; 14 s,l,r,num,v:array[0..maxn] of longint; 15 //========================= 16 procedure insert(var k:longint; p:longint); 17 begin 18 if k=0 then 19 begin 20 inc(m); 21 s[m]:=1; num[m]:=p; k:=m; 22 exit; 23 end; 24 inc(s[k]); 25 if v[p]<=v[num[k]] then insert(l[k],p) 26 else begin 27 inc(rak,s[l[k]]+1); 28 insert(r[k],p); 29 end; 30 end; 31 //========================= 32 function sele(var k:longint; t:longint):longint; 33 begin 34 if k=0 then exit(-1); 35 if s[l[k]]=t-1 then 36 begin 37 sele:=num[k]; 38 if (l[k]=0)or(r[k]=0) then 39 begin 40 k:=l[k]+r[k]; 41 exit; 42 end; 43 num[k]:=sele(l[k],s[l[k]]); 44 dec(s[k]); 45 exit; 46 end; 47 dec(s[k]); 48 if s[l[k]]>t-1 then exit(sele(l[k],t)) else exit(sele(r[k],t-s[l[k]]-1)); 49 end; 50 //========================= 51 begin 52 init; n:=0; root:=0; 53 while getpresent(cnt,luck,isboy) do 54 begin 55 inc(n); rak:=1; v[n]:=cnt; 56 insert(root,n); 57 if isboy then inc(rak,luck) else dec(rak,luck); 58 if (rak<=s[root])and(rak>0) then t:=sele(root,rak) else t:=-1; 59 if t>0 then 60 begin 61 dec(v[t]); if v[t]>0 then insert(root,t); 62 end; 63 tell(t); 64 end; 65 end.