hdu 3487

做了一天……內牛滿面……哭

參考資料:

http://dongxicheng.org/structure/splay-tree/

http://blog.csdn.net/liuwei_nefu/article/details/5901869


#include "stdio.h"
#include "stdlib.h"

typedef struct _Node{
	int val;
	int rev;
	int child;
	struct _Node* pre;
	struct _Node* ch[2];  //0,左孩子; 1,右孩子;
}Node, *pNode;

int n, m;
pNode root;

/*更新節點的孩子數*/
void update(pNode r){
	int lf, rt;
	lf = r->ch[0]?(r->ch[0]->child+1):0;
	rt = r->ch[1]?(r->ch[1]->child+1):0;
	r->child = lf+rt;
}

// void insert(pNode* r, int data){
// 	pNode w = *r;
// 	if(!w){
// 		w = (pNode)malloc(sizeof(Node));
// 		w->val = data;
// 		w->child = 0;
// 		w->rev = 0;
// 		w->pre = 0;
// 		w->ch[0] = w->ch[1] = 0;
// 		*r = w;
// 		return;
// 	}
// 	if(data > w->val){
// 		insert(&(w->ch[1]), data);
// 		if(w->ch[1]->child==0) w->ch[1]->pre = w;
// 	}else{
// 		insert(&(w->ch[0]), data);
// 		if(w->ch[0]->child==0) w->ch[0]->pre = w;
// 	}
// 	update(w);
// }

/*旋轉,0爲往左旋轉,1爲往右旋轉。旋轉要注意各種指針的更新,要仔細啊!*/
void rotate(pNode* r, int op){ //0,lf; 1,rt;
	pNode t, w = *r;
	t = w->ch[!op];  //獲得左/右孩子
	w->ch[!op] = t->ch[op]; 
	if(t->ch[op]) t->ch[op]->pre = w;
	t->ch[op] = w;
	t->pre = w->pre;
	if(w->pre){  //更新w父節點的孩子指針
		if(w == w->pre->ch[0])
			w->pre->ch[0] = t;
		else
			w->pre->ch[1] = t;
	}else
		root = t;  //這句非常重要!!!如果t旋轉到了最頂點,即w->pre爲0,那麼要更新root指向!
	w->pre = t;
	update(w);
	update(t);
	*r = t;  //將新的節點賦值到*r中
}

/*如果r被標記,則旋轉r的“直接”子樹,並將標記下傳到“直接”孩子*/
void push(pNode r){
	pNode t;
	if(r && r->rev){
		t = r->ch[0];
		r->ch[0] = r->ch[1];
		r->ch[1] = t;

		r->rev = 0;
		if(r->ch[0])
			r->ch[0]->rev ^= 1;
		if(r->ch[1])
			r->ch[1]->rev ^= 1;
	}
}

/*尋找中序遍歷的第k個節點*/
pNode find(int k, pNode r){
	int t;
	if(!r) return 0;
	push(r);
	t = r->ch[0]?(r->ch[0]->child+1):0;
	if(k==t+1)
		return r;
	if(k>t+1)
		return find(k-t-1, r->ch[1]);
	else
		return find(k, r->ch[0]);
}

/*伸展運動,將a旋轉到b的下面。當b爲0時,即,將a旋轉爲root*/
void splay(pNode a, pNode b){
	pNode k;
	k = a->pre;
	if(!k || k==b)
		return;

	if(a==k->ch[0])
		rotate(&k, 1);
	else
		rotate(&k, 0);
	splay(k, b);
}

// void show(pNode r){
// 	if(!r) return;
// 	show(r->ch[0]);
// 	printf("%d ", r->val);
// 	show(r->ch[1]);
// }

/*刪除樹,養成好習慣。不過要稍微多耗一點時間*/
void del(pNode r){
	if(!r) return;
	del(r->ch[0]);
	del(r->ch[1]);
	free(r);
}

/*將a~b切下來,放到c的後面*/
void cut(){
	int a, b, c;
	pNode tmp;
	scanf("%d %d %d", &a, &b, &c);
	splay(find(a, root), 0);  //將實際的第a-1個節點旋轉爲root
	splay(find(b+2, root), root); //將實際的第b+1個節點旋轉爲root的下面,即爲root的右孩子(因爲a<=b),這樣在a-1與b+1之間,即爲a~b,也就是b+1節點的左孩子!
	tmp = root->ch[1]->ch[0]; //將b+1節點的左孩子切下來!
	root->ch[1]->ch[0] = 0; 
	update(root->ch[1]); //更新切下後的節點
	update(root);

	splay(find(c+1, root), 0); //將實際的第c個節點旋轉爲root
	splay(find(c+2, root), root);  //將實際的第c+1個節點旋轉到root,即,第c個節點的下方。這時,第c+1個節點的左孩子肯定爲空!因爲比c+1小1的c,已經爲root了!
	root->ch[1]->ch[0] = tmp;  //將前面切下的接到第c+1個節點的左孩子
	tmp->pre = root->ch[1];
	update(root->ch[1]);  //更新接上節點後的節點
	update(root);
}

/*反轉!*/
void rvs(){
	int a, b;
	scanf("%d %d", &a, &b);
	splay(find(a, root), 0); //旋轉爲root
	splay(find(b+2, root), root); //旋轉到root的下面,那麼a-1與b+1之間,即b+1節點的左孩子,就是a~b
	root->ch[1]->ch[0]->rev ^= 1;  //只將b+1節點的直接左孩子標記即可,除非被下次訪問到,否則不需要馬上反轉!請看參考資料。
}

int mk;

void out(pNode r){
	if(!r) return;
	push(r);
	out(r->ch[0]);
	if(r->val>0 && r->val<n+1){
		if(mk)
			mk = 0;
		else
			printf(" ");
		printf("%d", r->val);
	}
	out(r->ch[1]);
}
/*使用二分初始化樹,這樣可以使樹高度最低!如果採用直接右插,建好的樹深度爲n+2,會爆棧溢出!*/
void init(pNode* r, int st, int ed, pNode pre){
	pNode w;
	int m = (st+ed)>>1;
	if(st>ed) return;

	w = (pNode)malloc(sizeof(Node));
	w->val = m;
	w->pre = pre;
	w->rev = 0;
	w->child = ed-st;
	w->ch[0] = w->ch[1] = 0;
	*r = w;
	
	init(&(w->ch[0]), st, m-1, w);
	init(&(w->ch[1]), m+1, ed, w);
}

void main(){
	char opt[5];
	int i;
	freopen("in.txt", "r", stdin);
	while(scanf("%d %d", &n, &m), !(n<0&&m<0)){
		root = 0;
		init(&root, 0, n+1, 0);  //注意啦!這裏插入的是0~n+1,共n+2個節點!爲啥呢?是爲了方便cut操作!所以如果要找第a個節點,實際要find第a+1個
		for(i=0; i<m; i++){
			getchar();
			scanf("%s", opt);
			if(opt[0]=='C')
				cut();
			else
				rvs();
		}
		mk = 1;
		out(root);
		printf("\n");
		del(root);
	}
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章