HDU 3487 Play with Chain [平衡树splay]

本文介绍了一种解决区间操作问题的方法,通过使用线段树实现区间翻转和区间移动的功能。具体包括如何构建线段树、进行区间翻转、区间移动等关键步骤,并提供了完整的AC代码。

题意:给一个1到n的序列,有两种操作:

①将区间【L,R】的序列截下来,放到截下来的之后的序列的第k个位置后面。

②将区间【L,R】的序列翻转。

题解:(注意要加首尾节点)要取区间的时候要先将L的前驱splay到根节点,R的后继splay到根的儿子节点,之后root的右儿子的左儿子就是区间【L,R】,然后打上翻转标记即可。

AC代码:

#include<stdio.h>  
#include<iostream>  
#include<string.h>
#include<algorithm>  
using namespace std;  
#define keytree ch[ch[root][1]][0]    
#define L(x) ch[x][0]    
#define R(x) ch[x][1]  
#define N 300010  
int ch[N][2],pre[N],cnt[N],size[N],val[N],small[N],rev[N],key[N];  
int tot,root;  
int a[N],n;  
void newnode(int &u,int fa,int w,int KEY)  
{  
    u=++tot;
    ch[u][0]=ch[u][1]=rev[u]=0;  
    pre[u]=fa;size[u]=1;  
    val[u]=small[u]=w;  
    key[u]=KEY;  
}  
void up(int u)  
{  
    size[u]=1+size[L(u)]+size[R(u)];  
    small[u]=min(val[u],min(small[L(u)],small[R(u)]));  
}  
void down(int u)  
{  
    if(rev[u])  
    {  
        if(L(u))rev[L(u)]^=1;  
        if(R(u))rev[R(u)]^=1;  
        swap(L(u),R(u));  
        rev[u]=0;  
    }  
}  
void rotate(int u,int kind)//kind表示u在fa的哪一边  
{  
    int fa=pre[u];  
    down(fa);down(u);  
    ch[fa][kind]=ch[u][!kind];  
    pre[ch[u][!kind]]=fa;  
    if(pre[fa])ch[pre[fa]][ch[pre[fa]][1]==fa]=u;  
    pre[u]=pre[fa];  
    ch[u][!kind]=fa;  
    pre[fa]=u;  
    up(fa);up(u);  
}  
void splay(int u,int goal)  
{  
    int fa,kind;  
    down(u);  
    while(pre[u]!=goal)  
    {  
        if(pre[pre[u]]==goal)  
        {  
            down(pre[u]);down(u);  
            rotate(u,R(pre[u])==u);  
        }  
        else   
        {  
            fa=pre[u];  
            down(pre[u]);down(fa);down(u);  
            kind=R(pre[fa])==fa;  
            if(ch[fa][kind]!=u)//不在同一侧   
            {  
                rotate(u,!kind);  
                rotate(u,kind);  
            }  
            else   
            {  
                rotate(fa,kind);  
                rotate(u,kind);  
            }  
        }  
    }  
    up(u);  
    if(goal==0)root=u;  
}  
int getkth(int u,int k)//第k个键值的点的编号   
{  
    down(u);  
    int s=size[L(u)]+1;  
    if(s==k) return u;  
    if(s>k) return getkth(L(u),k);  
    else return getkth(R(u),k-s);  
}    
int find(int u,int x)//查找键值为x的点的编号   
{                    // 有反转标记时不可用  
    down(u);  
    if(key[u]==x)return u;  
    if(key[u]>x)  
    {  
        if(!L(u))return -1;  
        return find(L(u),x);  
    }  
    if(key[u]<x)  
    {  
        if(!R(u))return -1;  
        return find(R(u),x);  
    }  
}  
int getpre(int u)  
{  
    down(u);u=L(u);down(u);  
    while(R(u))  
    {  
        u=R(u);  
        down(u);  
    }  
    return u;  
}  
int getnext(int u)  
{  
    down(u);u=R(u);down(u);  
    while(L(u))  
    {  
        u=L(u);  
        down(u);  
    }  
    return u;  
}  
void del(int x)//删除编号为x的节点   
{  
    if(cnt[x]>1)  
    {  
        cnt[x]--;  
        return ;  
    }  
    splay(x,0);  
    if(L(root))  
    {  
        int p=getpre(x);  
        splay(p,root);  
        R(p)=R(root);  
        pre[R(root)]=p;  
        root=p;  
        pre[p]=0;  
        up(root);  
    }  
    else  
    {  
        root=R(root);  
        pre[root]=0;  
    }  
}  
void build(int &u,int l,int r,int fa)//按pos为键值  
{                        //val为数的大小 a存数的大小  
    if(l>r)return ;  
    int mid=(l+r)>>1;  
    newnode(u,fa,a[mid],mid);  
    build(L(u),l,mid-1,u);  
    build(R(u),mid+1,r,u);    
    up(u);  
}  
void init()    
{  
    root=tot=0;  
    L(root)=R(root)=pre[root]=size[root]=rev[root]=0;  
    val[root]=small[root]=N;    
    newnode(root,0,N,0);  
    newnode(R(root),root,N,N);    
    build(keytree,1,n,R(root));  
    up(R(root));    
    up(root);    
}  
int getmin(int u,int x)//得到最小值的相对位置   
{  
    down(u);  
    if(val[u]==x) return 1+size[L(u)];  
    if(small[L(u)]==x) return getmin(L(u),x);  
    if(small[R(u)]==x) return size[L(u)]+1+getmin(R(u),x);  
}  
//--------------------------------------------------基本操作
int g=0;
void dfs(int root)
{
	if(root==0)return ;
	down(root);
	dfs(ch[root][0]);
	
	if(key[root]!=0&&key[root]!=N)
	{   g++;
		if(g<n)printf("%d ",key[root]);
		else printf("%d\n",key[root]);
	}
	dfs(ch[root][1]);
}   
int main()
{
	int m;
	while(~scanf("%d%d",&n,&m))
	{
		if(n<0&&m<0)break;
		for(int i=1;i<=n;i++)
			a[i]=i;
		g=0;
		init();
		for(int i=0;i<m;i++)
		{
			char op[10];
			scanf("%s",op);
			if(op[0]=='C')
			{
				int l,r,p;
				scanf("%d%d%d",&l,&r,&p);
				int pos=getkth(root,l);
				splay(pos,0);
				pos=getkth(root,r+2);
				splay(pos,root);
				int now=keytree;
				ch[ch[root][1]][0]=0;
				pos=getkth(root,p+1);
				splay(pos,0);
				pos=getkth(root,p+2);
				splay(pos,root);
				ch[ch[root][1]][0]=now;
				pre[now]=ch[root][1];
			}
			else
			{
				int l,r;
				scanf("%d%d",&l,&r);
				int pos=getkth(root,l);
				splay(pos,0);
				pos=getkth(root,r+2);
				splay(pos,root);
				rev[keytree]^=1;
			}
		}
		dfs(root);
	} 
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值