【HDU】3487 Play with Chain 【splay】

传送门:【HDU】3487 Play with Chain


题目分析:题目不难,注意细节就好,涉及到splay的区间剪切以及区间翻转操作。


代码如下:


/*
	本算法需要两个虚拟节点:0、n+1
	clear ()                                        初始化
	newNode ( int v , SplayNode* f )                新建节点并赋值v以及令其父亲为f
	init ( int n )                                  初始化
	rotate ( SplayNode* o , int d )                 旋转,d=0表示左旋,d=1表示右旋
	splay ( SplayNode* o , SplayNode* f )           伸展o直到o的父节点为f为止
	select ( int k , SplayNode* f )                 选择第k个元素并将其旋转到其父节点为f为止(不计虚拟节点)
	SplayNode* get ( int l , int r )                返回区间[l,r]:即将l-1旋转到根,将r+1旋转为l-1的右节点
	void split ( int l , int r , SplayNode* &o )    将区间[l,r]剪切到o
	void merge ( int pos , SplayNode* o )           将o插入到pos以及pos+1之间:即将pos旋转到根,pos+1旋转为pos的右节点
	void cut ( int l , int r , int pos )            将区间[l,r]剪切出来并插入到pos的右端
	void reverse ( int l , int r )                  翻转区间[l,r]
	void print ( SplayNode* o , int &cnt , int n )  中序输出整个区间
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

#define REP( i , a , b ) for ( int i = a ; i < b ; ++ i )
#define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define REV( i , a , b ) for ( int i = a ; i >= b ; -- i )
#define CLR( a , x ) memset ( a , x , sizeof a )

const int MAXN = 300005 ;

struct SplayNode {
	SplayNode *c[2] , *f ;
	int s , v , rev ;
	SplayNode () : s ( 0 ) {}
	void pushup () {
		s = c[0] -> s + c[1] -> s + 1 ;
	}
	void pushdown () {
		if ( rev ) {
			rev = 0 ;
			swap ( c[0] , c[1] ) ;
			c[0] -> rev ^= 1 ;
			c[1] -> rev ^= 1 ;
		}
	}
} Tnull , *null = &Tnull ;

struct Splay {
	SplayNode node[MAXN] , *cur , *root ;
	
	Splay () : cur ( node ) , root ( null ) {}
	
	void clear () {
		root = null ;
		cur = node ;
	}
	
	SplayNode* newNode ( int v , SplayNode* f ) {
		cur -> v = v ;
		cur -> s = 1 ;
		cur -> rev = 0 ;
		cur -> c[0] = cur -> c[1] = null ;
		cur -> f = f ;
		return cur ++ ;
	}
	
	void init ( int n ) {
		clear () ;
		FOR ( i , 0 , n + 1 ) {
			SplayNode* o = newNode ( i , null ) ;
			o -> c[0] = root ;
			root -> f = o ;
			root = o ;
			root -> pushup () ;
		}
	}
	//旋转
	void rotate ( SplayNode* o , int d ) {
		SplayNode* p = o -> f ;
		p -> pushdown () ;//先pushdown父节点
		o -> pushdown () ;//后pushdown自身
		p -> c[!d] = o -> c[d] ;
		o -> c[d] -> f = p ;
		o -> f = p -> f ;
		if ( p -> f != null ) {
			if ( p == p -> f -> c[0] ) p -> f -> c[0] = o ;
			else p -> f -> c[1] = o ;
		}
		o -> c[d] = p ;
		p -> f = o ;
		p -> pushup () ;
		if ( root == p ) root = o ;
	}
	//伸展o直到o的父节点为f为止
	void splay ( SplayNode* o , SplayNode* f ) {
		while ( o -> f != f ) {
			SplayNode* p = o -> f ;
			if ( p -> f == f ) {
				if ( o == p -> c[0] ) rotate ( o , 1 ) ;
				else rotate ( o , 0 ) ;
			} else {
				if ( p == p -> f -> c[0] ) {
					if ( o == p -> c[0] ) rotate ( p , 1 ) , rotate ( o , 1 ) ;
					else rotate ( o , 0 ) , rotate ( o , 1 ) ;
				} else {
					if ( o == p -> c[1] ) rotate ( p , 0 ) , rotate ( o , 0 ) ;
					else rotate ( o , 1 ) , rotate ( o , 0 ) ;
				}
			}
		}
		o -> pushup () ;
	}
	//选择第k个元素并将其旋转到其父节点为f为止(不计虚拟节点)
	void select ( int k , SplayNode* f ) {
		SplayNode* o = root ;
		++ k ;				//空出虚拟节点的位置
		while ( o != null ) {
			o -> pushdown () ;	//先pushdown
			int s = o -> c[0] -> s ;//后计算节点数
			if ( k == s + 1 ) break ;
			if ( k <= s ) o = o -> c[0] ;
			else {
				k -= s + 1 ;
				o = o -> c[1] ;
			}
		}
		splay ( o , f ) ;
	}
	//返回区间[l,r]
	SplayNode* get ( int l , int r ) {
		select ( l - 1 , null ) ;
		select ( r + 1 , root ) ;
		return root -> c[1] -> c[0] ;
	}
	//翻转区间[l,r]
	void reverse ( int l , int r ) {
		SplayNode* o = get ( l , r ) ;
		o -> rev ^= 1 ;
	}
	//将区间[l,r]剪切到o
	void split ( int l , int r , SplayNode* &o ) {
		o = get ( l , r ) ;
		o -> f = null ;
		root -> c[1] -> c[0] = null ;
		root -> c[1] -> pushup () ;
		root -> pushup () ;
	}
	//将o插入到pos以及pos+1之间
	void merge ( int pos , SplayNode* o ) {
		get ( pos + 1 , pos ) ;
		o -> f = root -> c[1] ;
		root -> c[1] -> c[0] = o ;
		root -> c[1] ->pushup () ;
		root -> pushup () ;
	}
	//将区间[l,r]剪切出来并插入到pos的右端
	void cut ( int l , int r , int pos ) {
		SplayNode* o ;
		split ( l , r , o ) ;
		merge ( pos , o ) ;
	}
	//中序输出整个区间
	void print ( SplayNode* o , int &cnt , int n ) {
		o -> pushdown () ;
		if ( o -> c[0] != null ) print ( o -> c[0] , cnt , n ) ;
		if ( o -> v && o -> v != n + 1 ) printf ( "%d%c" , o -> v , ++ cnt < n ? ' ' : '\n' ) ;
		if ( o -> c[1] != null ) print ( o -> c[1] , cnt , n ) ;
	}
} T ;

int n , m ;

void solve () {
	int l , r , pos ;
	char s[5] ;
	T.init ( n ) ;
	while ( m -- ) {
		scanf ( "%s%d%d" , s , &l , &r ) ;
		if ( s[0] == 'C' ) {
			scanf ( "%d" , &pos ) ;
			T.cut ( l , r , pos ) ;
		} else T.reverse ( l , r ) ;
	}
	int cnt = 0 ;
	T.print ( T.root , cnt , n ) ;
}

int main () {
	while ( ~scanf ( "%d%d" , &n , &m ) && ( n >= 0 || m >= 0 ) )
		solve () ;
	return 0 ;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值