HDOJ 3487 Play with Chain【splay】

题目:http://acm.hdu.edu.cn/showproblem.php?pid=3487

题意:给定一串序列,有两种操作:①把某段区间切到某一点后面②把某个区间颠倒

分析:这题由于涉及到区间移动,所以需要用到平衡树的旋转操作。于是新学了个splay(有个演示网站:http://www.cs.usfca.edu/~galles/visualization/SplayTree.html),学了半天还是没搞懂splay为何是平衡树。。

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
#define N 300050
using namespace std;
struct node
{
    node *ch[2],*p;
    int val,sz,rev;
    void init(int _v){ch[0]=ch[1]=p=NULL;val=_v;rev=0;sz=1;};
    bool d(){return p->ch[1]==this;}
    void setch(int d,node *nc){ch[d]=nc;if(nc)nc->p=this;}
    void updata()
    {
        sz = 1;
        if(ch[0]) sz += ch[0]->sz;
        if(ch[1]) sz += ch[1]->sz;
    }
    void pushdown()
    {
        if(rev) {
            swap(ch[0], ch[1]); rev = 0;
            if(ch[0]) ch[0]->rev ^= 1;
            if(ch[1]) ch[1]->rev ^= 1;
        }
    }
}tree[N],*root,*cur;

int num,n,m;

node* maken(int v)
{
    cur->init(v);
    return cur++;
}

node* build(int l,int r)
{
    if(l > r) return NULL;
    int m = (l + r) >> 1;
    node *x = maken(m);
    x->setch(0, build(l, m-1));
    x->setch(1, build(m+1, r));
    x->updata();
    return x;
}

void show(node *u){
    u->pushdown();
    if(u->ch[0])show(u->ch[0]);
    if(num>0&&num<=n){
        printf("%d",u->val);
        printf("%c",num==n?'\n':' ');
    }
    num++;
    if(u->ch[1])show(u->ch[1]);
}

node* select(int k)//找到点权为v的点
{
    node *x=root;
    while(true) {
        x->pushdown();
        int tmp = x->ch[0]?x->ch[0]->sz:0;
        if(k == tmp) break;
        if(k < tmp) x = x->ch[0];
        else x = x->ch[1], k-=tmp+1;
    }
    return x;
}

void rot(node *x)
{
    node *y=x->p, *z=y->p;
    y->pushdown();x->pushdown();
    int d=x->d();
    if(z) z->setch(y->d(), x);
    y->setch(d, x->ch[!d]); x->setch(!d, y); y->updata();
    if(!z) root=x, x->p=NULL;
}

void splay(node *st,node *en)
{
    while(st->p!=en)rot(st);st->updata();
}

void print()
{
        for(int i=0;i<=n+1;i++)
        {
            cout<<tree[i].val<<' ';
            if(tree[i].ch[0])cout<<tree[i].ch[0]->val<<' ';
            if(tree[i].ch[1])cout<<tree[i].ch[1]->val;
            cout<<endl;
        }
}

int main()
{
    while(scanf("%d%d",&n,&m)&&(n>=0||m>=0))
    {
        cur=tree;root=build(0,n+1);
        char cmd[5];
        while(m--)
        {
            int l,r;scanf("%s%d%d",cmd,&l,&r);
            node *ln=select(l-1),*rn=select(r+1);
            splay(ln,NULL);splay(rn,ln);
            node *bn=rn->ch[0];
            if(cmd[0]=='C'){
                int pos;scanf("%d",&pos);
                rn->setch(0,NULL);rn->updata();ln->updata();//把目标区间截出来
                node *lpn=select(pos),*rpn=select(pos+1);
                splay(lpn,NULL);splay(rpn,lpn);
                rpn->setch(0,bn);rpn->updata();lpn->updata();//把目标区间所在的子树接上
            }
            else if(bn)bn->rev^=1;

        }num=0;show(root);
          // print();
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值