关闭

splay学习总结

标签: splayNOIP总结
158人阅读 评论(6) 收藏 举报
分类:

Splay(伸展树) 学习总结:

 

理解:

Splay是一种平衡二叉排序树, 复杂度均摊log n

 

单点操作Splay要点:

1、动态内存静态化

实现:(内存池回收分配):

template
struct memorypool 
{
    T buf[size], *tail, *st[size];
    int top;
    memorypool() : top(0), tail(buf) {}
    inline T *alloc() { return top ? st[--top] : tail++; }
    /*内存分配*/ 
    inline void recycle(T *p) { st[top++] = p; }
    /*内存回收*/ 
};

2、结点信息存储

实现:

struct node
{
	node *child[2], **root, *parent;
	/*child[]: 左右儿子, root: 根节点地址, 方便修改, parent: 父节点*/ 
	int count, size; /*count: 当前值个数, size: 以当前点为根的子树大小*/ 
	T value;
}

3、初始化结点

实现:

inline void init(node *parent, const T &value, node **root)
{
	this->parent = parent, this->root = root, this->value = value;
	this->count = 1, child[L] = NULL, child[R] = NULL;
}

4、删除节点

实现:

/*将前驱旋转到根,然后将后继旋转到前
驱的儿子,这时后继的左儿子为当前节点
,注意删除后维护节点信息*/
inline void erase(node *v)
{
	if(v->count != 1) return v->splay(), v->count--, v->maintain();
	node *precursor = v->precursor(), *successor = v->successor();
	precursor->splay(), successor->splay(precursor), successor->child[L]->recycle(pool);
	pool.recycle(successor->child[L]) , successor->child[L] = NULL;
	successor->maintain(), precursor->maintain();
}

5、旋转 & 旋转到根

实现

/*将自己旋转到原来的父亲的位置,将自己原来
的右儿子变为原来的父亲的左儿子,自己现在的
右儿子变为原来的父亲, 最后注意更新root*/
inline void rotate()
{
    Relation x = relation();
    node *oldparent = parent;
	if(oldparent->parent) oldparent->parent->child[oldparent->relation()] = this ;
	parent = oldparent->parent, oldparent->child[x] = child[x ^ 1];
	if(child[x ^ 1]) child[x ^ 1]->parent=oldparent;
	child[x ^ 1] = oldparent, oldparent->parent = this, oldparent->maintain(), maintain();
	if(!parent)  *root = this;
}

inline void splay(node *targetparent = NULL)
{
	while(parent != targetparent)
	{
		if(parent->parent == targetparent) rotate();
		else if(parent->relation() == relation()) parent->rotate(), rotate();
		else rotate(), rotate();
	}
}

6、求解前驱 &后继

实现

inline node *precursor()
{
    splay();
	node *v = child[L];
	while(v->child[R]) v = v->child[R];
	return v;
}
inline node *successor()
{
	splay();
	node *v = child[R];
	while(v->child[L]) v = v->child[L];
	return v;
}

7、选择第K小元素

实现:

/*最开始的边界有一个-INF注意加1*/
inline node *select(int k)
{
	k++;
	node *v = root;
	while(!(v->rank() < k && v->rank() + v->count >= k))
		v=(v->rank() >= k ? v->child[L] : (k -= v->rank() + v->count, v->child[R]));
	return v->splay(), v;
}

模板题目:

维护集合

题目背景

superOJ420

Source

#include
#include
#include
#include
#include
#include
#include

using namespace std;

template
struct memorypool 
{
    T buf[size], *tail, *st[size];
    int top;
    memorypool() : top(0), tail(buf) {}
    inline T *alloc() { return top ? st[--top] : tail++; }
    inline void recycle(T *p) { st[top++] = p; }
};
 
const int MAXN = 1000000; 

template 
struct Splay
{
	enum Relation { L = 0, R = 1 };
	struct node
	{
		node *child[2], **root, *parent;
		int count, size; 
		T value;
		inline void init(node *parent, const T &value, node **root)
		{
			this->parent = parent, this->root = root, this->value = value;
			this->count = 1, child[L] = NULL, child[R] = NULL;
		}
		inline void recycle(memorypool  &pool)
		{
			if(child[L]) pool.recycle(child[L]);
			if(child[R]) pool.recycle(child[R]);
		}
		inline Relation relation() { return this == parent->child[L] ? L : R; }
		inline void maintain() { size = (child[L] ? child[L]->size : 0) + (child[R] ? child[R]->size : 0) + count; }
		inline void rotate()
		{
			Relation x = relation();
			node *oldparent = parent;
			if(oldparent->parent) oldparent->parent->child[oldparent->relation()] = this ;
			parent = oldparent->parent, oldparent->child[x] = child[x ^ 1];
			if(child[x ^ 1]) child[x ^ 1]->parent=oldparent;
			child[x ^ 1] = oldparent, oldparent->parent = this, oldparent->maintain(), maintain();
			if(!parent)  *root = this;
		}
		inline void splay(node *targetparent = NULL)
		{
			while(parent != targetparent)
			{
				if(parent->parent == targetparent) rotate();
				else if(parent->relation() == relation()) parent->rotate(), rotate();
				else rotate(), rotate();
			}
		}
		inline node *precursor()
		{
			splay();
			node *v = child[L];
			while(v->child[R]) v = v->child[R];
			return v;
		}
		inline node *successor()
		{
			splay();
			node *v = child[R];
			while(v->child[L]) v = v->child[L];
			return v;
		}
		inline int rank() { return !child[L] ? 0 : child[L]->size; }
	} *root;
	memorypool  pool;
	Splay() : root(NULL) { insert(INF), insert(-INF); }
	inline node *find(const T &value)
	{
		node *v = root;
		while(v && value != v->value) v = (value < v->value ? v->child[L] : v->child[R]);
		return !v ? NULL : (v->splay(), v);
	}
	inline node *insert(const T &value)
	{
		node *v = find(value);
		if(v) return v->count++, v->maintain(), v;
		node **target = &root, *parent = NULL;
		while(*target) parent = *target, parent->size++, target = (value < parent->value ? &parent->child[L] : &parent->child[R]);
		return *target = pool.alloc(), (*target)->init(parent, value, &root), (*target)->splay(), root;
	}
	inline void erase(const T &value) { erase(find(value)); }
	inline void erase(node *v)
	{
		if(v->count != 1) return v->splay(), v->count--, v->maintain();
		node *precursor = v->precursor(), *successor = v->successor();
		precursor->splay(), successor->splay(precursor), successor->child[L]->recycle(pool);
		pool.recycle(successor->child[L]) , successor->child[L] = NULL;
		successor->maintain(), precursor->maintain();
	}
	inline int rank(const T &value) 
	{
		node *v = find(value);
		if(v) return v->rank();
		else
		{
			v = insert(value);
			register int ans = v->rank();
			return erase(v), ans;
		}
	}
	inline node *select(int k)
	{
		k++;
		node *v = root;
		while(!(v->rank() < k && v->rank() + v->count >= k))
			v=(v->rank() >= k ? v->child[L] : (k -= v->rank() + v->count, v->child[R]));
		return v->splay(), v;
	}
	inline const T &precursor(const T &value)
	{
		node *v = find(value);
		if(v) return v->precursor()->value;
		else
		{
			v = insert(value);
			const T &ans = v->precursor()->value;
			return erase(v), ans;
		}
	}
	inline const T &successor(const T &value)
	{
		node *v = find(value);
		if(v) return v->successor()->value;
		else
		{
			v = insert(value);
			const T &ans = v->successor()->value;
			return erase(v),ans;
		}
	}
};

inline void R(int &v)
{
	char c = 0;
	bool p = true;
	v = 0;
	while(!isdigit(c))
	{
		if(c == '-')
			p = false;
		c = getchar();
	}
	while(isdigit(c))
	{
		v = (v << 3) + (v << 1) + (c ^ '0');
		c = getchar(); 
	}
	if(!p)
		v = -v;
}

const int INF = ~0u>>1;

Splay splay;
int n;

int main()
{
	R(n);
	while(n--)
	{
		int type, x, ans;
		R(type), R(x);
		switch(type)
		{
			case 0:
				splay.insert(x);
				break;
			case 1:
				splay.erase(x);
				break;
			case 2:
				cout << splay.select(x)->value << '\n';
				break;
			case 3:
				cout << splay.rank(x) - 1 << '\n';
				break;
			case 4:
				ans = splay.precursor(x);
				cout << (ans == -INF ? -1 : ans) << '\n';
				break;
			case 5:
				ans = splay.successor(x);
				cout << (ans == INF ? -1 : ans) <<'\n';
				break;
		}
	}
	return 0;
}


区间操作Splay要点:

1、动态内存静态化 (同上)

2、节点信息存储

实现

struct node
{
	node *child[2], *parent, **root;
	int size;
	T value;
	bool reverse, bound;
	/*reverse:反转标记,bound:边界*/
}

3、建立边界

实现

/*左右边界*/
inline void buildbound(Relation x)
{
	node **v = &root, *parent = NULL;
	while(*v) parent = (*v), parent->size++, v = &parent->child[x];
	(*v) = pool.alloc(), (*v)->init(parent, &root, 0, true), (*v)->maintain();
}

4、提取区间

实现:

inline node *select(int k)
{
	k++;
	node *v = root;
	while(v->pushdown(), v->rank() + 1 != k) v = (v->rank() >= k ? v->child[L] : (k -= v->rank() + 1, v->child[R]));
	return v->splay(), v;
}
/*类比对于一个点的删除时的提取*/
inline node *&select(int l, int r)
{
	node *vl = select(l - 1), *vr = select(r + 1);
	return vl->splay(), vr->splay(vl), vr->child[L];
}

5、插入区间

实现:

inline node *buildrange(const T *a, int l, int r, node *parent)
{
	if(l > r) return NULL;
	register int mid = l + r >> 1;
	node *v = pool.alloc();
	v->init(parent, &root, a[mid - 1]);
	if(l != r) v->child[L] = buildrange(a, l, mid - 1, v), v->child[R] = buildrange(a, mid+1, r, v);
	return v->maintain(), v; 
}

inline void insert(int pos, const T *a, int n)
{
	node *&range = select(pos + 1, pos);
	range = buildrange(a, 1, n, root->child[R]);
	root->child[R]->maintain(), root->maintain();
}

6、删除区间

实现:

/*提取区间后,类比单点删除*/
inline void erase(int l, int r)
{
	node *&range = select(l, r);
	range->recycle(pool), pool.recycle(range), range = NULL;
	root->child[R]->maintain(), root->maintain();
}

7、区间反转

实现:

/*对于标记的处理和更新*/
inline void pushdown()
{
	if(reverse)
	{
		if(child[L]) child[L]->reverse ^= 1;
		if(child[R]) child[R]->reverse ^= 1;
		swap(child[L], child[R]), reverse = false;
	}
}

inline void reverse(int l, int r)
{
	node *range = select(l ,r);
	range->reverse ^= 1;
}

文本编辑器

题目背景

BZOJ1269

Source:

 

#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

inline void R(int &v)
{
	char c = 0;
	bool p = true;
	v = 0;
	while(!isdigit(c))
	{
		if(c == '-')
			p = false;
		c = getchar();
	}
	while(isdigit(c))
	{
		v = (v << 3) + (v << 1) + (c ^ '0');
		c = getchar();
	}
	if(!p)
		v = -v;
}

template
struct memorypool {
    T buf[size], *tail, *end, *st[size];
    int top;
    memorypool() : top(0), tail(buf), end(buf + size) {}
    inline T *alloc() {
        if (top) return st[--top];
        if (tail != end) return tail++;
        return new T;
    }
    inline void recycle(T *p) { 
        if (top > size) delete p;
        else st[top++] = p;
    }
};
const int MAXN = 1024 * 1024 * 4 ;
const int MAX = 1024 * 1024 * 4 ;

template
struct Splay
{
	enum Relation { L = 0, R = 1 };
	struct node
	{
		node *child[2], *parent, **root;
		int size;
		T value;
		bool reverse, bound;
		inline void init(node *parent, node **root, const T &value, bool bound = false)
		{
			this->parent = parent, this->root = root, this->value = value, this->bound = bound;
			this->reverse = false, this->size = 1, child[L] = child[R] = NULL;
		}
		inline void recycle(memorypool &pool)
		{
			if(child[L]) pool.recycle(child[L]);
			if(child[R]) pool.recycle(child[R]);
		}
		inline void pushdown()
		{
			if(reverse)
			{
				if(child[L]) child[L]->reverse ^= 1;
				if(child[R]) child[R]->reverse ^= 1;
				swap(child[L], child[R]), reverse = false;
			}
		}
		inline Relation relation() { return this == parent->child[L] ? L : R; }
		inline void maintain() { pushdown(), size = (child[L] ? child[L]->size : 0) + (child[R] ? child[R]->size : 0) + 1; }
		inline void rotate()
		{
			if(parent->parent) parent->parent->pushdown();
			parent->pushdown(), pushdown();
			Relation x = relation();
			node *oldparent = parent;
			if(oldparent->parent) oldparent->parent->child[oldparent->relation()] = this;
			parent = oldparent->parent, oldparent->child[x] = child[x ^ 1];
			if(child[x ^ 1]) child[x ^ 1]->parent = oldparent;
			child[x ^ 1] = oldparent, oldparent->parent = this, oldparent->maintain(), maintain();
			if(!parent) *root = this; 
		}
		inline void splay(node *targetparent = NULL)
		{
			while(parent != targetparent)
			{
				if(parent->parent == targetparent) rotate();
				else
				{
					parent->parent->pushdown(), parent->pushdown();
					if(parent->relation() == relation()) parent->rotate(), rotate();
					else rotate(), rotate();
				}
			}
		}
		inline int rank() { return child[L] ? child[L]->size : 0; }
		inline int pos() { return splay(), child[L] ? child[L]->size : 0; }
	} *root;
	memorypool  pool;
	Splay() : root(NULL) { buildbound(L), buildbound(R); }
	inline node *buildrange(const T *a, int l, int r, node *parent)
	{
		if(l > r) return NULL;
		register int mid = l + r >> 1;
		node *v = pool.alloc();
		v->init(parent, &root, a[mid - 1]);
		if(l != r) v->child[L] = buildrange(a, l, mid - 1, v), v->child[R] = buildrange(a, mid+1, r, v);
		return v->maintain(), v; 
	}
	inline void buildbound(Relation x)
	{
		node **v = &root, *parent = NULL;
		while(*v) parent = (*v), parent->size++, v = &parent->child[x];
		(*v) = pool.alloc(), (*v)->init(parent, &root, 0, true), (*v)->maintain();
	}
	inline node *select(int k)
	{
		k++;
		node *v = root;
		while(v->pushdown(), v->rank() + 1 != k) v = (v->rank() >= k ? v->child[L] : (k -= v->rank() + 1, v->child[R]));
		return v->splay(), v;
	}
	inline node *&select(int l, int r)
	{
		node *vl = select(l - 1), *vr = select(r + 1);
		return vl->splay(), vr->splay(vl), vr->child[L];
	}
	inline void erase(int l, int r)
	{
		node *&range = select(l, r);
		range->recycle(pool), pool.recycle(range), range = NULL;
		root->child[R]->maintain(), root->maintain();
	}
	inline void insert(int pos, const T *a, int n)
	{
		node *&range = select(pos + 1, pos);
		range = buildrange(a, 1, n, root->child[R]);
		root->child[R]->maintain(), root->maintain();
	}
	inline void reverse(int l, int r)
	{
		node *range = select(l ,r);
		range->reverse ^= 1;
	}
	inline int size() { return root->size - 2; }
};

Splay splay;

int main()
{
	static char type[10], a[MAX], c;
	static int x, pos, n;
	R(n);
	while(n--)
	{
		scanf("%s", type);
		if(type[0] == 'I') 
		{
			R(x);
			int cnt = 0;
			while(true)
			{
				c = getchar();
				if(c >= 32 && c <= 126)
					a[cnt++] = c;
				if(cnt == x) break;
			}
			splay.insert(pos, a, x);
		}
		else if(type[0] == 'M') R(x), pos = x;
		else if(type[0] == 'D') R(x), splay.erase(pos + 1, pos + x);
		else if(type[0] == 'R') R(x), splay.reverse(pos + 1, pos + x);
		else if(type[0] == 'G') cout << splay.select(pos + 1)->value << '\n';
		else if(type[0] == 'P') pos--;
		else if(type[0] == 'N') pos++;
	}
	return 0;
}  

 

Summary

Splay是目前流行的平衡树算法之一, 在做题中有着非常广泛的应用, 但是常数大也是一个严重的问题, 所以本蒟蒻就强行学习了神犇的Splay纯指针写法,%%%Menci, 现在数据结构在OI中的比重越来越大,所以唯有不断理解学习才是王道。

参考:https://oi.men.ci/splay-template/

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:14712次
    • 积分:1697
    • 等级:
    • 排名:千里之外
    • 原创:154篇
    • 转载:0篇
    • 译文:0篇
    • 评论:50条
    博客专栏