Online JudgeProblem SetAuthorsOnline ContestsUser
Web Board
Home Page
F.A.Qs
Statistical Charts
Problems
Submit Problem
Online Status
Prob.ID:
Register
Update your info
Authors ranklist
Current Contest
Past Contests
Scheduled Contests
Award Contest
User ID:
Password:
  Register

雁过留声——伸展树的应用

Posted by fanhqme at 2009-10-05 17:01:45 on Problem 3580
首先感谢IceKingdom!
只有像他这样的好心人多一些,
我们这个社会才会更加和谐!

题目的叙述是相当“裸”的,明明白白地告诉,
要实现一个datastructure,支持几种操作。

一般来说,用于“折腾”序列的数据结构主要有以下几个:
链表
块状链表
伸展树
很明显,这题伸展树是一个绝佳的选择。

伸展树的一个重要的“基本”操作是“砍树”,即
把一个区间[x,y]的所有节点组织成一个子树。
这个实现是:
查找y+1
查找x-1
y+1的作子树即为所求!
当然,这个在实现的时候是需要特判三种情况的...
如果re了,不妨想想自己是否偷懒了。

对于区间Add,照搬线段树的思想,对每一个节点打一个delta标记。
我的习惯是:
delta表示他的子孙中需要再增加的值,与自身无关。
这样,对于每一个add,把区间对应的子树“砍”出来,
根节点的delta key min三个值统统增加。

对于区间Reverse,继续打标记,记录rev表示他的子树是否需要翻转
注意:rev标记也与自身无关

Ins就用最朴素的写法好了,完成后不要忘记splay

Delete的实现比较简单:
把它“砍”成一个子树,断开与父亲的链接,最后splay上去。
注意:要特判!

Revolve比较“复杂”
首先,我们把它转换为交换区间[a,b] [b+1,c]
先把[a,c]砍出来,然后在子树内部(就是把这个子树假想成原树)
查找c-a
查找b+1-a
之后把b+1-a的左子接到c-a的右子上,更新c-a b+1-a
这里还是有特判!!!

由于要不停的处理标记,所以一个好的标记系统是必须的。
我的策略:对每一个节点,定义updata()与down(),分别来更新于下传标记
struct node{
	int sz,mn,delta,key;
	char rev;
	node *lc,*rc,*fa;
	node(){
		sz=1;delta=0;rev=0;
		lc=rc=fa=NULL;
	}
	void updata(){
		sz=1;
		if (lc)sz+=lc->sz;
		if (rc)sz+=rc->sz;
		mn=key;
		if (lc)if (mn>lc->mn+delta)mn=lc->mn+delta;
		if (rc)if (mn>rc->mn+delta)mn=rc->mn+delta;
	}
	void down(){
		node *p;
		if (rev){
			if (lc){
				lc->rev^=1;
				p=lc->lc;lc->lc=lc->rc;lc->rc=p;
			}
			if (rc){
				rc->rev^=1;
				p=rc->lc;rc->lc=rc->rc;rc->rc=p;
			}
			rev=0;
		}
		if (delta){
			if (lc){
				lc->key+=delta;
				lc->delta+=delta;
				lc->mn+=delta;
			}
			if (rc){
				rc->key+=delta;
				rc->delta+=delta;
				rc->mn+=delta;
			}
			delta=0;
		}
	}
}epool[NMax];

沿着路径向下走的时候,沿路down,
splay向上调整的时候,沿路updata
struct Splay{
	node *root,*etop;
	void zig(node *p){
		node *q=p->fa;
		if (q==root)root=p;
		q->lc=p->rc;
		if (p->rc)p->rc->fa=q;
		p->rc=q;p->fa=q->fa;q->fa=p;
		if (p->fa){
			if (p->fa->lc==q)p->fa->lc=p;
			else p->fa->rc=p;
		}
		q->updata();p->updata();
	}
	void zag(node *p){
		node *q=p->fa;
		if (q==root)root=p;
		q->rc=p->lc;
		if (p->lc)p->lc->fa=q;
		p->lc=q;p->fa=q->fa;q->fa=p;
		if (p->fa){
			if (p->fa->lc==q)p->fa->lc=p;
			else p->fa->rc=p;
		}
		q->updata();p->updata();
	}
	Splay(){
		root=NULL;
		etop=epool;
	}
	void splay(node *p){
		p->down();
		while (p!=root){
			p->updata();
			if (p->fa==root){
				if (p->type()==0)zig(p);
				else zag(p);
				break;
			}else if (p->fa->fa==root){
				if (p->type()==0)zig(p);
				else zag(p);
				p->updata();
				if (p->type()==0)zig(p);
				else zag(p);
				break;
			}{
				if (p->type()==0){
					if (p->fa->type()==0){
						zig(p->fa);
						zig(p);
					}else{
						zig(p);
						zag(p);
					}
				}else{
					if (p->fa->type()==0){
						zag(p);
						zig(p);
					}else{
						zag(p->fa);
						zag(p);
					}
				}
			}
		}
		root->updata();
		root->fa=NULL;
	}
	node *find(int x){
		node *p;
		p=root;
		p->down();
		while (!(p->lc && p->lc->sz==x) && !(!p->lc && x==0)){
			if (p->lc && p->lc->sz>x){
				p=p->lc;
			}
			else{
				if (p->lc)x-=p->lc->sz;
				x--;
				p=p->rc;
			}
			p->down();
		}
		splay(p);
		return p;
	}
	void Ins(int x,int a){
		node *p;
		if (!root){
			root=etop;
			etop->key=a;
			etop->updata();
			etop++;
		}else{
			p=root;
			while (1){
				p->down();
				if (p->lc && p->lc->sz>=x)p=p->lc;
				else if (!x){
					p->lc=etop;etop->fa=p;
					etop->key=a;etop->updata();
					splay(etop);
					etop++;
					break;
				}else if (p->rc){
					if (p->lc)x-=p->lc->sz;
					x--;
					p=p->rc;
				}else{
					p->rc=etop;etop->fa=p;
					etop->key=a;etop->updata();
					splay(etop);
					etop++;
					break;
				}
			}
		}
	}
WA的同志可以参考一下我的代码

最后在操作中,要绞尽脑汁特判,否则re得让人哭
	void Reverse(int a,int b){
		if (a>=b)return;
		node *p,*t;
		p=cuttree(a,b);
		t=p->lc;p->lc=p->rc;p->rc=t;
		p->rev^=1;
	}
	void Del(int a){
		node *p,*q;
		if (!a){
			p=find(a);
			root=p->rc;
			if (root)root->fa=NULL;
		}else if (a==root->sz-1){
			p=find(a);
			root=p->lc;
			if (root)root->fa=NULL;
		}else{
			p=find(a-1);
			q=find(a+1);
			p->rc=NULL;
			p->updata();q->updata();
		}
	}
	void Revolve(int a,int b,int c){//exchange [a,b] [b+1,c]
		if (a>=c || a>b || b>=c)return;
		node *p,*bf,*br,*q,*t,*r;
		p=cuttree(a,c);
		bf=p->fa;br=root;
		root=p;p->fa=NULL;
		if (b+1==c){
			q=find(c-a);
			t=q->lc;q->lc=q->rc;q->rc=t;
		}else{
			q=find(c-a);
			r=find(b+1-a);
			q->rc=r->lc;
			r->lc=NULL;
			q->rc->fa=q;
			q->updata();
			p->updata();
		}
		root->fa=bf;
		if (bf){
			if (bf->lc==p)bf->lc=root;
			else bf->rc=root;
			root=br;
		}
	}
};

最后,要记得REVOLVE的第三个参数可能很变态

以上的代码帮助我得到了7xxmsAC,
一看rank,发现自己写的常数其实不错...:)
再次感谢IceKingdom,他的5xxmsAC估计是加了无数针对性优化刷出来的吧
(*^__^*) 

Followed by:

Post your reply here:
User ID:
Password:
Title:

Content:

Home Page   Go Back  To top


All Rights Reserved 2003-2013 Ying Fuchen,Xu Pengcheng,Xie Di
Any problem, Please Contact Administrator