[HNOI2004]宠物收养所

Splay求前驱与后继,然后累加和即可。

书上的Splay求x的前驱与后继需要插入x之后才能求得前驱与后继。删除操作时把x与前驱或者后继删除即可。

 

#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <string>
using namespace std;

typedef int Comparable;

struct Node
{
	Node *left, *right;
	int element;
	Node(int v=0, Node *lt=0, Node *rt=0):element(v),left(lt),right(rt) {}
};

Node *null = new Node();

void rotateWithLeftChild(Node *&t)
{
	Node *k = t->left;
	t->left = k->right;
	k->right = t;
	t = k;
}

void rotateWithRightChild(Node *&t)
{
	Node *k = t->right;
	t->right = k->left;
	k->left = t;
	t = k;
}

void splay(Node *&t, const Comparable &x)
{
	Node *leftTreeMax, *rightTreeMin;
	
	static Node head;
	
	head.left = head.right = null;
	
	leftTreeMax = rightTreeMin = &head;
	null->element = x;
	
	for(;;)
	{
		if(x < t->element)
		{
			if(x < t->left->element)
				rotateWithLeftChild(t);
			if(t->left == null) break;
			
			//Link Right
			rightTreeMin->left = t;
			rightTreeMin = t;
			t = t->left;
		}
		else if(x > t->element)
		{
			if(x > t->right->element)
				rotateWithRightChild(t);
			if(t->right == null) break;
			
			//Link left
			leftTreeMax->right = t;
			leftTreeMax = t;
			t = t->right;
		}
		else break;
	}
	
	leftTreeMax->right = t->left;
	rightTreeMin->left = t->right;
	
	t->left = head.right;
	t->right = head.left;
}

int insert(Node *&root, const Comparable &x)
{
	Node* newNode = new Node(x);
	
	if(root == null)
	{
		newNode->left = newNode->right = null;
		root = newNode;
		return 0;
	}
	
	splay(root, x);
	if(x < root->element)
	{
		newNode->left = root->left;
		newNode->right = root;
		root->left = null;
		root = newNode;
	}
	else if(x > root->element)
	{
		newNode->right = root->right;
		newNode->left = root;
		root->right = null;
		root = newNode;
	}
	else return -1;
	
	newNode = NULL;
	
	return 1;
}

void printTree(Node *o)
{
	if(o != null)
	{	
		printf("%d ", o->element);
		printTree(o->left);
		printTree(o->right);
	}
}

void remove(Node *&o, int x)
{
	splay(o, x);
	if(o->element != x) return ;
	
	Node *newTree;
	if(o->left == null)
		newTree = o->right;
	else
	{
		newTree = o->left;
		splay(newTree, x);
		newTree->right = o->right;
	}
	
	delete o;
	o = newTree;
}

Node *findMin(Node *o)
{
	if(o == null) return null;
	while(o->left != null)
		o = o->left;
	return o;
}

Node *findMax(Node *o)
{
	if(o == null) return null;
	while(o->right != null)
		o = o->right;
	return o;
}

Node *predecessor(Node *o, int x)
{
	splay(o, x);
	return findMax(o->left);
}

Node *successor(Node *o, int x)
{
	return findMin(o->right);
}

///////////////////////
int n;
const int INF = 0x3f3f3f3f;

void readint(int &x)
{
	char c = getchar();
	while(!isdigit(c)) c = getchar();
	
	x = 0;
	while(isdigit(c))
	{
		x = x*10+c-'0';
		c = getchar();
	}
}

int main()
{
	while(~scanf("%d", &n))
	{
		Node *root = null; root->left = null; root->right = null;
		
		int ans = 0, flag = -1;
		for(int i = 1; i <= n; i++)
		{
			int cmd, x; scanf("%d%d", &cmd, &x);
			if(flag == -1)
			{
				insert(root, x);
				flag = cmd;
			}
			else if(flag == cmd)
			{
				insert(root, x);
			}
			else
			{
				int p = insert(root, x);
				if(p == -1)
				{
					remove(root, x);
				}
				else
				{
					Node *pre = predecessor(root, x);
					Node *next = successor(root, x);
					int t1 = pre != null? pre->element:INF;
					int t2 = next != null? next->element:INF;
					if(abs(t1-x) <= abs(t2-x))
					{
						ans = (ans+abs(t1-x))%1000000;
						remove(root, t1);
						remove(root, x);
					}
					else
					{
						ans = (ans+abs(t2-x))%1000000;
						remove(root, t2);
						remove(root, x);
					}
				}
				if(root == null) flag = -1;
			}
		}
		printf("%d\n", ans%1000000);
	}
	return 0;
}

Splay伸展树

                         Splay伸展树

Splay是二叉查找树,不是一颗平衡树,但可以通过自调整达到一种近似平衡的情况。Splay在连续M次操作中均摊时间复杂度是M*logn,其中n为节点的个数,M为操作的次数。Splay的操作与特点将在以下介绍。

  • 当访问一个节点时,立即把当前访问的节点旋转到根节点,让访问(插入)的节点立马成为新的根节点。
  • 旋转操作与AVL很像,有两种旋转,左旋、右旋。
  • Splay操作有两种不同的实现方式。一种是自底向上的旋转,需要记录当前节点的父亲节点。另外一种是自顶向上的实现方式,不需要记录父亲节点。
  • 由于Splay把当前访问的节点放在根节点,很容易求出当前节点的前驱与后继。
  • Splay支持merge、split、findMin、findMax、predecessor、successor、insert、remove等操作。
  • 在信息学竞赛中,Splay可以实现能实现线段树的所有的功能,即pushup、pushdown维护区间信息在Splay中用的很活,但Splay又支持快速的序列分离和合并,这是线段树做不到的。
  • 验证自己手写的数据结构是否正确可以去OJ写一写用到该数据结构的题。

继续阅读

Treap树

最近在学高级数据结构,AVL、BST、SPLAY、Treap、红黑树等,能够快速实现AVL、BST、SPLAY、Treap,以及会利用简单变形解决某些问题就达到要求了。

大学计算机专业的学生,数据结构书上的所有数据结构能够快速实现,对计算机理论不至于精通,但要熟悉,随便提出一个基础理论至少不陌生。高数、现代、概率论都要达到熟悉的地步。

Treap,顾名思义,Tree+Heap的简称,与BST不同,节点还附加了一个随机优先级来平衡树,所以又带有堆的性质,可以证明最坏的查找、删除效率为O(logn),它可以做到set做不到的事,如快速求第K大值,以及求值x的名次,也被称作名次树。如果附加的优先级是固定的话,那么就成了笛卡尔树

 

继续阅读