伸展树(排列变换,uva 11922)

数据结构日常RE。

多自己带数据测就是王道吧。


解题思路:

一开始不会嘛,只好想办法套大白书上给的splay,split和merge代码。但要注意的是splay函数里的那个cmp和前面treap里的cmp不一样。splay的是比序号(也就是第几个的意思),treap的是比大小(BST的查找嘛)。所以要自己重写。然后想该怎么套咧?题目要求复制后逆序粘贴到后面,那么显然树上的节点不能同时保存值与序号(不能把值和它的位置一起记录在一个节点里),否则merge的时候就得一个节点一个节点的去维护,那就跟纯暴力算法没区别啦。所以想只能维护值或序号中的某一个。那该维护哪一个咧?我们发现伸展树,它是不存在什么维持平衡的要求(根本就不是一种平衡树啊,它只是一个BST啊),那么树的形态是任意的,从而也就不需要维护什么优先值啊,什么深度啊之类的东西了,随便你怎么rotate(rotate是一种可以保持BST性质的一种树的变换吧)。而在merge函数里我们发现竟然可以在简单调整后直接粘上去就OK了(代码上有一句注释:假定left的所有元素比right小。一开始自己一直很不理解,凭啥这么假定呢?显然多数情况下,这都不成立呀。事实上确实如此,如果你调用了这个merge函数,那么在绝大多数情况下,树将会失去BST的性质。但是本题只是希望你能理解伸展算法在树的分裂与合并中的应用,所以只是将伸展树中的一部分抽了出来成了一题。在这题中的伸展树是不具备BST性质的。但只需要通过伸展算法,便可完成树的分裂与合并。真正的伸展树可上网查,几乎都是把重心放在了树的调整而不是分裂与合并上。)。说明树的结构和形态自然而然地记录了序号,那么我们只需要定义一个树的遍历方法(如中序遍历),以记录序号。然后在节点上记录具体的值就好了。所以先中序遍历生成一个树,然后按要求split与merge,最后中序便遍历一下输出就好啦。


一些疑惑:

不知道为什么splay操作非要这么转,讨论得那么麻烦,直接递归转上来不行吗,如下,而且我也AC啦,详见代码2

void splay(Node* &o,int k)
{
    o->push_down();
    int d=o->cmp(k);
    if(d==-1) return;
    else if(d==0)
    {
        splay(o->ch[0],k);
        rotate(o,1);
    }
    else if(d==1)
    {
        splay(o->ch[1],k-(o->ch[0]?o->ch[0]->s:0)-1);
        rotate(o,0);
    }
}



代码

#include<bits/stdc++.h>
using namespace std;

struct Node
{
    Node* ch[2];
    int v,s,r;

    Node(int x)
    {
        v=x;
        r=0;
        s=1;
        ch[0]=ch[1]=NULL;
    }

    int cmp(int x)
    {
        if(ch[0])
        {
            if(ch[0]->s>=x) return 0;
            else if(ch[0]->s+1==x) return -1;
            else return 1;
        }
        else
        {
            if(x==1) return -1;
            else return 1;
        }
    }
    void maintain()
    {
        if(!this) return;
        s=1;
        if(ch[0]) s+=ch[0]->s;
        if(ch[1]) s+=ch[1]->s;
    }
    void push_down()
    {
        if(!this) return;
        if(r)
        {
            r=0;
            swap(ch[0],ch[1]);
            if(ch[0]) ch[0]->r^=1;
            if(ch[1]) ch[1]->r^=1;
        }
    }
};

void rotate(Node* &o,int d)
{
    o->push_down();
    Node* k=o->ch[d^1];
    k->push_down();
    o->ch[d^1]=k->ch[d];
    k->ch[d]=o;
    o->maintain();
    k->maintain();
    o=k;
}

void splay(Node* &o,int k)
{
    o->push_down();
    int d=o->cmp(k);
    if(d==1) k-=(o->ch[0]?o->ch[0]->s:0)+1;
    if(d!=-1)
    {
        Node* p=o->ch[d];
        p->push_down();
        int d2=p->cmp(k);
        int k2=d2==0?k:k-(p->ch[0]?p->ch[0]->s:0)-1;
        if(d2!=-1)
        {
            splay(p->ch[d2],k2);
            if(d==d2) rotate(o,d^1);
            else rotate(o->ch[d],d);
        }
        rotate(o,d^1);
    }
}

Node* merge(Node* left,Node* right)
{
    if(left)
    {
        splay(left,left->s);
        left->ch[1]=right;
        left->maintain();
    }
    else left=right;
    return left;
}

void split(Node* o,int k,Node* &left,Node* &right)
{
    if(!k)
    {
        left=NULL;
        right=o;
        return;
    }
    splay(o,k);
    left=o;
    right=o->ch[1];
    o->ch[1]=NULL;
    left->maintain();
}

Node* init(int l,int r)
{
    if(l>r) return NULL;
    int m=l+(r-l)/2;
    Node* ret=new Node(m);
    ret->ch[0]=init(l,m-1);
    ret->ch[1]=init(m+1,r);
    ret->maintain();
    return ret;
}

void dfs(Node* o)
{
    if(!o) return;
    o->push_down();
    dfs(o->ch[0]);
    printf("%d\n",o->v);
    dfs(o->ch[1]);
}

int main()
{
    int n,m;
    int a,b;
    scanf("%d %d",&n,&m);
    Node* o=init(1,n);
    Node *le,*ri,*mi;
    while(m--)
    {
        scanf("%d %d",&a,&b);
        split(o,a-1,le,ri);
        split(ri,b-a+1,mi,ri);
        mi->r^=1;
        o=merge(le,ri);
        o=merge(o,mi);
    }
    dfs(o);
    return 0;
}


代码2

#include<bits/stdc++.h>
using namespace std;

struct Node
{
    Node* ch[2];
    int v,s,r;

    Node(int x)
    {
        v=x;
        r=0;
        s=1;
        ch[0]=ch[1]=NULL;
    }

    int cmp(int x)
    {
        if(ch[0])
        {
            if(ch[0]->s>=x) return 0;
            else if(ch[0]->s+1==x) return -1;
            else return 1;
        }
        else
        {
            if(x==1) return -1;
            else return 1;
        }
    }
    void maintain()
    {
        if(!this) return;
        s=1;
        if(ch[0]) s+=ch[0]->s;
        if(ch[1]) s+=ch[1]->s;
    }
    void push_down()
    {
        if(!this) return;
        if(r)
        {
            r=0;
            swap(ch[0],ch[1]);
            if(ch[0]) ch[0]->r^=1;
            if(ch[1]) ch[1]->r^=1;
        }
    }
};

void rotate(Node* &o,int d)
{
    o->push_down();
    Node* k=o->ch[d^1];
    k->push_down();
    o->ch[d^1]=k->ch[d];
    k->ch[d]=o;
    o->maintain();
    k->maintain();
    o=k;
}

void splay(Node* &o,int k)
{
    o->push_down();
    int d=o->cmp(k);
    if(d==-1) return;
    else if(d==0)
    {
        splay(o->ch[0],k);
        rotate(o,1);
    }
    else if(d==1)
    {
        splay(o->ch[1],k-(o->ch[0]?o->ch[0]->s:0)-1);
        rotate(o,0);
    }
}

Node* merge(Node* left,Node* right)
{
    if(left)
    {
        splay(left,left->s);
        left->ch[1]=right;
        left->maintain();
    }
    else left=right;
    return left;
}

void split(Node* o,int k,Node* &left,Node* &right)
{
    if(!k)
    {
        left=NULL;
        right=o;
        return;
    }
    splay(o,k);
    left=o;
    right=o->ch[1];
    o->ch[1]=NULL;
    left->maintain();
}

Node* init(int l,int r)
{
    if(l>r) return NULL;
    int m=l+(r-l)/2;
    Node* ret=new Node(m);
    ret->ch[0]=init(l,m-1);
    ret->ch[1]=init(m+1,r);
    ret->maintain();
    return ret;
}

void dfs(Node* o)
{
    if(!o) return;
    o->push_down();
    dfs(o->ch[0]);
    printf("%d\n",o->v);
    dfs(o->ch[1]);
}

int main()
{
    int n,m;
    int a,b;
    scanf("%d %d",&n,&m);
    Node* o=init(1,n);
    Node *le,*ri,*mi;
    while(m--)
    {
        scanf("%d %d",&a,&b);
        split(o,a-1,le,ri);
        split(ri,b-a+1,mi,ri);
        mi->r^=1;
        o=merge(le,ri);
        o=merge(o,mi);
        //dfs(o);
    }
    dfs(o);
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值