hdu3487 (splay伸展树 区间翻转,切割,插入)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string.h>
#include <queue>
#define N 300010
using namespace std;  //hdu3487 (splay伸展树 区间翻转,切割,插入) 
int pre[N], key[N], ch[N][2], size[N], rev[N], root, tot, n, cnt; //rev是翻转的延时标志
/*
伸展树的一点心得---   rotate、Splay操作是不影响伸展树原有的性质(只要是节点的左孩子,那它就小于节点,节点的右孩子那他就大于节点)
			判断节点(k)在其父亲节点(x)的左孩子还是右孩子时用ch[x][ch[x][1]==k]=k
			只要是建立起了一棵满足伸展树性质的,那都可以使用rotate、Splay操作,伸展树性质不变
			另外对于要pushdown或update,都要记得时常更新,否则很容易TLE
*/
void update(int x)  //区间维护
{
    size[x]=size[ch[x][0]]+size[ch[x][1]]+1;
    return ;
}
void pushdown(int x)  //传递下去
{
    if(rev[x])
    {
        int k=ch[x][0];
        ch[x][0]=ch[x][1];
        ch[x][1]=k;
        rev[ch[x][0]]^=1;
        rev[ch[x][1]]^=1;
        rev[x]=0;
    }
    return ;
}
void newnode(int &r, int father, int k)   //初始化
{
    r=++tot;
    pre[r]=father;
    key[r]=k;
    ch[r][0]=ch[r][1]=rev[r]=0;
    size[r]=1;
    return ;
}
void build(int l, int r, int father, int &k)  //分治法,建立伸展树
{
    if(l>r)return ;
    int mid=(l+r)>>1;
    newnode(k, father, mid);
    build(l, mid-1, k, ch[k][0]);
    build(mid+1, r, k, ch[k][1]);
    update(k);
    return ;
}
void init()//数据初始化,开头建立两个节点,一个是伸展树的根(相当于最小),一个是伸展树根的右节点(相当于最大)----作为边界对程序操作无影响,因为你截取的区间都在这中间
{
    root=tot=ch[0][0]=ch[0][1]=pre[0]=size[0]=rev[0]=0;
    newnode(root, 0, -1);
    newnode(ch[root][1], root, -1);
    update(root);
    build(1, n, ch[root][1], ch[ch[root][1]][0]);
    return ;
}
void rotate(int x, int kind)
{
    int k=pre[x];
    pushdown(k);
    pushdown(x);
    ch[k][!kind]=ch[x][kind];
    pre[ch[x][kind]]=k;
    ch[x][kind]=k;
    pre[x]=pre[k];
    if(pre[k])
        ch[pre[k]][ch[pre[k]][1]==k]=x;
    pre[k]=x;
    update(k);
    return ;
}
void Splay(int x, int goal)
{
    int k, g, h;
    pushdown(x);
    while(pre[x]!=goal)
    {
        k=pre[x];
        if(pre[k]==goal)
        {
            rotate(x, ch[k][0]==x);
        }
        else 
        {
            g=(ch[k][0]==x?0:1);
            h=(ch[pre[k]][0]==k?0:1);
            if(g==h)
            {
                rotate(k, !g);
                rotate(x, !g);
            }
            else
            {
                rotate(x, !g);
                rotate(x, g);
            }
        }
    }
    if(goal==0)root=x;
    update(x);
    return ;
}
int select(int k, int r)
{
	pushdown(r);
    if(size[ch[r][0]]+1==k)
        return r;
    else if(size[ch[r][0]]+1>k)
        return select(k, ch[r][0]);
    else return select(k-size[ch[r][0]]-1, ch[r][1]);
}
void reversal(int l, int r)
{
    int j, g;
    j=select(l, root);
    g=select(r+2, root);
    Splay(j, 0);
    Splay(g, root);
    rev[ch[g][0]]^=1;    //异或
    return ;
}
int getmin(int r)
{
	int s=r;
	pushdown(r);        //注意pushdown下去,否则TLE
	while(r)
	{
		s=r;
		r=ch[r][0];
		pushdown(r);
	}
	return s;
}

void cut(int l, int r, int k)  //切割区间并插入
{
    int j, g, h;
    j=select(l, root); //因为开头已经建立了一个最小的节点,每个输入的元素的排名要+1
    g=select(r+2, root);
    Splay(j, 0);
    Splay(g, root);
    h=ch[g][0]; //其左子树就是实际[l,r]区间里的数
    ch[g][0]=0;
    update(g);
    update(j);
    j=select(k+1, root);
	Splay(j, 0);
	g=getmin(ch[root][1]);
	Splay(g, root);
	ch[g][0]=h;//相当于插到k后面
	pre[ch[g][0]]=g;
	update(g);
	update(j);
	return ;
}

void print(int r) //输出,递归从伸展树的左边到伸展树的右边
{
    if(cnt==n)return ;
    if(r==0)return ;
    pushdown(r);
    print(ch[r][0]);
    if(key[r]>0)
    {
        printf("%d", key[r]);
        cnt++;
        if(cnt<n)printf(" ");
    }
    print(ch[r][1]);
}


int main()
{
    int m, a, b, c;
    char p[10];
    while(scanf("%d%d", &n, &m)!=EOF)
    {
        if(n<0&&m<0)break;
        init();
        while(m--)
        {
            scanf("%s", &p);
            if(p[0]=='C')
            {
                scanf("%d%d%d", &a, &b, &c);
                cut(a, b, c);
            }
            if(p[0]=='F')
            {
                scanf("%d%d", &a, &b);
                reversal(a, b);
            }
        }
        cnt=0;
        print(root);
        printf("\n");
    }
    return 0;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值