无比强大的数据结构 伸展树总结

链接 :http://www.notonlysuccess.com/index.php/splay-tree/

论文链接:http://www.docin.com/p-62465596.html


其实本来不想学splay树的,因为好像平时做题不怎么用到,但是,请注意,书到用时方恨少啊,多一点储备,就多一分机会

论文里说,动态树也要用splay来维护的说,有的斜率优化的题也要用splay来优化,所以,这几天我将我找到的splay的题目刷了下,整理如下


我觉得伸展树的作用应该分为两类,一类主要体现在维护区间的作用上,而另一类则是和普通的平衡树一样

首先介绍一下普通平衡树都有的功能


普通平衡树的功能主要有   

 插入 删除 一个数

 找 前驱  后继  第k大 小

求大于等于或小于等于某个数的个数(可以求逆序数)

确定一个数的排名

我找了一些基本的数据结构题 ,用 treap 和 splay 都实现了下,效率差不多

CSDN代码没有收缩功能,所以就不放这里了

splay的普通平衡树功能 :  模板在这里


下面是splay最重要的功能,对区间各种性质的维护

notonlysuccess的博客里已经有了很详细的介绍和一些习题,我就把做过的题贴上来吧

普通平衡树功能,找第k大,插入删除等    郁闷的出纳员   题解

找前驱 后继 删除一个节点,直接将要删除的节点旋转到根,然后删除根节点即可       宠物收养所:  题解

营业额统计   题解

区间更新,求和,模板题

区间翻转   题解

Queue-jumpers 

  要注意离散化的方法,其他操作对伸展树来说就相当于模拟      题解

区间切割插入     题解


上面的题都做了就可以挑战下下面两个重口味的题目了

poj 3580        这道题会用到伸展树的各种操作,主要是交换两个相邻的区间,直接切割一个区间,然后再插入相应的位置即可  题解

维修数列       这题的最大子列和 和  区间翻转结合起来 制造了一个巨大的坑,如果发现不了,下面这组数据可以告诉你为什么,不过最好还是自己去发现,

6 20
259 -231 -919  241  -676 -978
MAKE-SAME 4 3 772
REVERSE 2 5
MAX-SUM

维修数列的题解在这里

呵呵,伸展树搞定了,可以开工其他以前可望不可即的数据结构了

伸展树的模板

#include<cstdio>
typedef __int64 lld;
const int inf = ~0u>>2;
#define L ch[x][0]
#define R ch[x][1]
#define KT (ch[ch[rt][1]][0])
const int maxn = 222222;
struct SplayTree{
	int ch[maxn][2];
	int pre[maxn],sz[maxn],val[maxn];
	int rt,top;
	void Rotate(int x,int f) {
		int y = pre[x];
		down(y);   down(x);
		ch[y][!f] = ch[x][f];
		pre[ ch[x][f] ] = y;
		pre[x] = pre[y];
		if(pre[x]) ch[ pre[y] ][ ch[pre[y]][1] == y ] = x;
		ch[x][f] = y;
		pre[y] = x;
		up(y);
	}
	void Splay(int x,int goal) {
		down(x);
		while(pre[ x ] != goal ) {
			down( pre[pre[x]] ); down( pre[x] ); down(x);
			if(pre[ pre[x] ] == goal) Rotate( x , ch[ pre[x] ][0] == x );
			 else  {
				int y = pre[x] ,z = pre[y];
				int f = ( ch[z][0] == y);
				if(ch[y][f] == x) Rotate( x , !f ),Rotate( x , f );
				else 	Rotate( y , f ),Rotate( x , f );
			}
		}
		up(x);
		if(goal == 0) rt = x;
	}
	void RTO(int k,int goal) {
		int x=rt;
		down(x);
		while(sz[ L ] + 1 != k) {
			if(k < sz[ L ] + 1)  x = L ;	
			else {
				k -= (sz[ L ] + 1);
				x = R;
			}
			down(x);
		}
		Splay(x,goal);
	}
	void vist(int x){
		if(x){
			printf("结点%2d : 左儿子  %2d   右儿子  %2d  val: %2d sum=%I64d\n",x,ch[x][0],ch[x][1],val[x],sum[x]);
			vist(L);
			vist(R);
		}
	}
	void debug() {
		puts("");	vist(rt);  puts("");
	}
	void up(int x) {
		sz[x] = 1 + sz[ L ] + sz[ R ];
		sum[x] = val[x] + sum[ L ] + sum[ R ];
	}
	void down(int x) {
		if(add[x]) {
			val[ L ] += add[ x ];
			val[ R ] += add[ x ];
			add[ L ] += add[ x ];
			add[ R ] += add[ x ];
			sum[ L ] += (lld)add[x] * sz[ L ];
			sum[ R ] += (lld)add[x] * sz[ R ];
			add[ x ] = 0;
		}
	}
	void Newnode(int &x,int c,int f) {
		x=++top;
		L = R  = 0;  sz[x]=1;	pre[x]=f;

		val[x] = sum[x] = c;
	    add[x] = 0;
	}
	
	void build(int &x,int l,int r,int f) {
		if(l>r) return ;
		int m=l+r>>1;
		Newnode(x , num[m] , f);
		build( L , l , m-1 , x);
		build( R , m + 1, r , x);
		up(x);
	}
	void init(int n) {
	    ch[0][0]=ch[0][1]=pre[0]=0;
		sz[0]=rt=top=0;

		add[0]=sum[0]=0;
		
		Newnode(rt,-1,0);
		Newnode(ch[rt][1],-1,rt);
		sz[rt]=2;
		for(int i=1;i<=n;i++) scanf("%d",&num[i]);

		build(KT,1,n,ch[rt][1]);
		up(ch[rt][1]);  up(rt);
	}
	void update() {
		int l,r,c;
		scanf("%d%d%d",&l,&r,&c);
		RTO(l,0);
		RTO(r+2,rt);
		add[KT] += c;
		val[KT] += c;
		sum[KT] += (lld) c * sz[KT];
	}
	void query() {
		int l,r;
		scanf("%d%d",&l,&r);
		RTO(l,0);
		RTO(r+2,rt);
		printf("%I64d\n",sum[KT]);
	}
	lld sum[maxn];
	int add[maxn];
	int num[maxn];
}spt;
int main() {
	int m,n;char op[5];
	scanf("%d%d",&n,&m);
	spt.init(n);
	while(m--) {
	    scanf("%s",op);
		if(op[0]=='Q') spt.query();
		else spt.update();
	}
	return 0;
}
/*
4 10
1 2 3 4
Q 2 4
*/


你可能感兴趣的:(无比强大的数据结构 伸展树总结)