HDU 2475 Box(splay)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2475

题意:给出一棵树,两种操作:(1)修改某棵子树的父节点;(2)查询某个节点的根节点。

思路:每个题目中的箱子看做两个箱子i和i+n,(i,i+n)之间的为在i箱子中的。对于第一种操作,比如将u的父节点改为v,首先将u转到根节点,然后将u+n根节点右孩子,然后u子树就可以被整体切下。然后将另外两部分连接在一起。然后将v转到根,将v之后最左的节点转到v右孩子,然后将u插入v右孩子的左孩子。查询直接将u转到根,找到最左孩子。



struct node
{
    int id;
    node *c[2],*f;
};

node a[N],*nullNode;
int n,m;



void rotate(node *x,int k)
{
    node *y=x->f;
    y->c[k]=x->c[!k];
    y->c[k]->f=y;
    x->f=y->f;
    if(y->f->c[0]==y) y->f->c[0]=x;
    else y->f->c[1]=x;
    y->f=x;
    x->c[!k]=y;
}

void splay(node *x,node *y)
{
    while(x->f!=y)
    {
        if(x->f->f==y)
        {
            if(x->f->c[0]==x) rotate(x,0);
            else rotate(x,1);
        }
        else if(x->f->f->c[0]==x->f)
        {
            if(x->f->c[0]==x) rotate(x->f,0);
            else rotate(x,1);
            rotate(x,0);
        }
        else
        {
            if(x->f->c[1]==x) rotate(x->f,1);
            else rotate(x,0);
            rotate(x,1);
        }
    }
}



void change(int u,int v)
{
    if(u==v) return;
    splay(a+u,nullNode);
    splay(a+u+n,a+u);
    node *p;
    if(v!=0)
    {
        p=a+v;
        while(p!=nullNode)
        {
            if(p==a[u+n].c[0]) return;
            p=p->f;
        }
    }
    node *x=a[u].c[0],*y=a[u+n].c[1];
    a[u].c[0]=nullNode;
    a[u+n].c[1]=nullNode;
    x->f=y->f=nullNode;
    if(x!=nullNode&&y!=nullNode)
    {
        while(y->c[0]!=nullNode) y=y->c[0];
        y->c[0]=x;
        x->f=y;
    }
    if(v==0) return;
    splay(a+v,nullNode);
    p=a[v].c[1];
    while(p->c[0]!=nullNode) p=p->c[0];
    splay(p,a+v);
    p->c[0]=a+u;
    a[u].f=p;
}

int query(int x)
{
    splay(a+x,nullNode);
    node *p=a+x;
    while(p->c[0]!=nullNode) p=p->c[0];
    return p->id;
}

int get()
{
    int x=0,flag=1;
    char c=getchar();
    while(!isdigit(c)) c=getchar();
    if(c=='-')
    {
        flag=-1,c=getchar();
        while(!isdigit(c)) c=getchar();
    }
    while(isdigit(c))
    {
        x=x*10+c-'0';
        c=getchar();
    }
    return x*flag;
}


vector<int> g[N];
int isRoot[N];

node *build(int u)
{
    node *pre=a+u,*last=a+u,*cur;
    int i,v;
    FOR0(i,SZ(g[u]))
    {
        v=g[u][i];
        last=build(v);
        pre->c[1]=a+v;
        a[v].f=pre;
        pre=last;
    }
    cur=a+u+n;
    last->c[1]=cur;
    cur->f=last;
    return a+u+n;
}

void init()
{
    nullNode=new node();
    nullNode->c[0]=nullNode->c[1]=nullNode->f=nullNode;
    int i,u;
    FOR1(i,n) g[i].clear();
    clr(isRoot,0);
    FOR1(i,(n+n))
    {
        a[i].c[0]=a[i].c[1]=a[i].f=nullNode;
        a[i].id=i;
    }
    FOR1(i,n)
    {
        u=get();
        if(u==0) isRoot[i]=1;
        else g[u].pb(i);
    }
    node *p;
    FOR1(i,n) if(isRoot[i])
    {
        build(i);
        a[i].f=nullNode;
    }
}

int main()
{
    bool flag=false;
    while(scanf("%d",&n)!=-1)
    {
        if(flag) puts("");
        else flag=true;
        init();
        int u,v;
        m=get();
        char op[10];
        while(m--)
        {
            scanf("%s",op);
            if(op[0]=='M')
            {
                u=get(),v=get();
                change(u,v);
            }
            else u=get(),PR(query(u));
        }
    }
    return 0;
}

  

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值