1. 程式人生 > >洛谷 P2596 [ZJOI2006]書架 解題報告

洛谷 P2596 [ZJOI2006]書架 解題報告

ID 維護 get 個數 hang 交換 構造 play query

P2596 [ZJOI2006]書架

題目描述

小T有一個很大的書櫃。這個書櫃的構造有些獨特,即書櫃裏的書是從上至下堆放成一列。她用1到n的正整數給每本書都編了號。

小T在看書的時候,每次取出一本書,看完後放回書櫃然後再拿下一本。由於這些書太有吸引力了,所以她看完後常常會忘記原來是放在書櫃的什麽位置。不過小T的記憶力是非常好的,所以每次放書的時候至少能夠將那本書放在拿出來時的位置附近,比如說她拿的時候這本書上面有X本書,那麽放回去時這本書上面就只可能有X-1、X或X+1本書。

當然也有特殊情況,比如在看書的時候突然電話響了或者有朋友來訪。這時候粗心的小T會隨手把書放在書櫃裏所有書的最上面或者最下面,然後轉身離開。

久而久之,小T的書櫃裏的書的順序就會越來越亂,找到特定的編號的書就變得越來越困難。於是她想請你幫她編寫一個圖書管理程序,處理她看書時的一些操作,以及回答她的兩個提問:(1)編號為X的書在書櫃的什麽位置;(2)從上到下第i本書的編號是多少。

輸入輸出格式

輸入格式:

第一行有兩個數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本書的編號。

輸出格式:

對於每一條Ask或Query語句你應該輸出一行,一個數,代表詢問的答案。

說明

100%的數據,n,m <= 80000


這是我做的第一道非板子的平衡樹題目,寫+調大概花了2個小時,稍稍有點困,先以時間壓到1個小時為目標了。

很顯然,這顆平衡樹是一顆區間樹,用來平衡的域是書的相對位置大小,我們用樹的大小信息來查詢某個點在書架的相對位置,而不去直接存儲這個相對位置以比較,這應該是區間樹的一個重要特點。

\(pos[i]\)維護編號為\(i\)的書對應哪個節點。

對於操作:
放在最上面/下面就先刪掉然後加點
添加的話只有兩個情況,就先把要添加的節點伸展到根,然後和它的前驅/後繼交換即可
兩個詢問互為逆操作,有關排名的


#include <cstdio>
#include <iostream>
#define ls t[now].ch[0]
#define rs t[now].ch[1]
#define f t[now].par
#define s t[now].ch[typ]
using namespace std;
const int N=160010;
struct Splay
{
    int ch[2],par,siz,num;
}t[N];
int root,tot=0,n,m,x,a[N],pos[N];//編號為i的書對應的當前點的編號
string S;
int identity(int now)
{
    return t[f].ch[1]==now;
}
void connect(int fa,int now,int typ)
{
    f=fa;
    t[fa].ch[typ]=now;
}
void updata(int now)
{
    t[now].siz=t[ls].siz+t[rs].siz+1;
}
void rotate(int now)
{
    int p=f,typ=identity(now);
    connect(p,t[now].ch[typ^1],typ);
    connect(t[p].par,now,identity(p));
    connect(now,p,typ^1);
    updata(p),updata(now);
}
void splay(int now,int to)
{
    to=t[to].par;
    for(int typ;f!=to;rotate(now))
        if(t[f].par!=to)
            rotate(identity(now)==identity(f)?f:now);
    if(!to) root=now;
}
int New(int dat)
{
    t[++tot].num=dat;t[tot].siz=1;return tot;
}
void free(int now)
{
    t[now].num=0;t[now].siz=0;t[now].par=0;ls=0;rs=0;
    if(tot==now) tot--;
}
void find(int x)//當前排名為x的書的編號
{
    int now=root;
    while(t[ls].siz+1!=x)
        now=t[now].ch[t[ls].siz+1<x?x-=t[ls].siz+1,1:0];
    splay(now,root);
    printf("%d\n",t[now].num);
}
int get_max(int now,int typ)
{
    if(typ) t[now].siz++;
    return rs?get_max(rs,typ):now;
}
int get_min(int now,int typ)
{
    if(typ) t[now].siz++;
    return ls?get_min(ls,typ):now;
}
void extrack(int now)//刪除編號為now的點
{
    splay(now,root);
    if(!ls){root=rs;connect(0,root,1);free(now);return;}
    int rt=get_max(ls,0);
    splay(rt,ls);
    connect(rt,rs,1);
    connect(0,rt,1);
    updata(rt);
    root=rt;
    free(now);
}
void rank(int now)
{
    splay(now,root);
    printf("%d\n",t[ls].siz);
}
void top(int now,int num)//節點編號和書的編號
{
    extrack(now);
    int fa=get_min(root,1);
    connect(fa,now=New(num),0);
    pos[num]=now;
    updata(now);
}
void bottom(int now,int num)
{
    extrack(now);
    int fa=get_max(root,1);
    connect(fa,now=New(num),1);
    pos[num]=now;
    updata(now);
}
void exchange(int to,int now)
{
    swap(pos[t[to].num],pos[t[now].num]);
    swap(t[to].num,t[now].num);
}
void insert(int now,int T)
{
    if(!T) return;
    splay(now,root);
    if(T==1) exchange(get_min(rs,0),now);
    else exchange(get_max(ls,0),now);
}
int build(int l,int r,int fa)
{
    if(l>r) return 0;
    int now=l+r>>1;
    t[now].par=fa;
    t[now].num=a[now];
    updata(now);
    if(l==r) return now;
    ls=build(l,now-1,now);
    rs=build(now+1,r,now);
    updata(now);
    return now;
}
/*void write(int now)
{
    if(!now) return;
    write(ls);
    printf("%d ",t[now].num);
    write(rs);
}*/
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",a+i);
        pos[a[i]]=i;
    }
    connect(0,root=build(1,n,0),1);
    tot=n;
    for(int i=1;i<=m;i++)
    {
        cin>>S;
        scanf("%d",&x);
        if(S=="Top")
            top(pos[x],x);
        else if(S=="Bottom")
            bottom(pos[x],x);
        else if(S=="Insert")
        {
            int T;scanf("%d",&T);
            insert(pos[x],T);
        }
        else if(S=="Ask")
            rank(pos[x]);
        else
            find(x);
        //write(root);
        //printf("%d\n");
    }
    return 0;
}

2018.6.14

洛谷 P2596 [ZJOI2006]書架 解題報告