HDU 1890 Robotic Sort(Splay tree)

本文介绍了一种使用Splay树解决区间翻转问题的方法,并详细展示了Splay树的各种操作,包括旋转、维护和调整过程。作者还分享了调试过程中遇到的问题及解决方案。

转载请注明出处,谢谢http://blog.youkuaiyun.com/ACM_cxlove?viewmode=contents by---cxlove

对区间有反转,同样用懒惰标记,SPT比较方便。

依次考虑第K大的,将其旋转至根,左子树的数量便是需要反转的,之后把根删除即可

难道是我的伸展函数有问题,一直TLE,注释部分的求大神解释。

换了一个Splay函数就过了。。。郁闷啊,debug到死

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define N 100015
#define inf 1<<29
#define LL long long
#define Key_value ch[ch[root][1]][0]
using namespace std;
int pre[N],ch[N][2],root,tot1;  //分别表示父结点,键值,左右孩子(0为左孩子,1为右孩子),根结点,结点数量
int size[N];    //分别表示子树规模,内存池,内存池容量
int rev[N];
int n,q;
inline void NewNode(int &r,int father,int k){
	r=k;
	pre[r]=father;
	rev[r]=0;
	size[r]=1;
	ch[r][0]=ch[r][1]=0;  //左右孩子为空
}
//将延迟标记更新到孩子结点
inline void Push_Down(int r){
	if(rev[r]){
		rev[ch[r][0]]^=1;
		rev[ch[r][1]]^=1;
		swap(ch[r][0],ch[r][1]);
		rev[r]=0;
	}
}
//通过孩子结点更新父结点
inline void Push_Up(int r){
	size[r]=size[ch[r][0]]+size[ch[r][1]]+1;
}
//旋转,kind为1为右旋,kind为0为左旋
inline void Rotate(int x,int kind){
	int y=pre[x];
	Push_Down(y);
	Push_Down(x);
	//类似SBT,要把其中一个分支先给父节点
	ch[y][!kind]=ch[x][kind]; 
	pre[ch[x][kind]]=y;
	//如果父节点不是根结点,则要和父节点的父节点连接起来
	pre[x]=pre[y];
	if(pre[x])
		ch[pre[y]][ch[pre[y]][1]==y]=x;
	ch[x][kind]=y;
	pre[y]=x;
	Push_Up(y);
}inline void zig(int x){
	int y=pre[x], z=pre[y];
	ch[y][1]=ch[x][0]; pre[ch[x][0]]=y;
	ch[x][0]=y; pre[y]=x;
	pre[x]=z;
	if(z) ch[z][ch[z][1]==y]=x;
	Push_Up(y);
}
inline void zag(int x){
	int y=pre[x], z=pre[y];
	ch[y][0]=ch[x][1]; pre[ch[x][1]]=y;
	ch[x][1]=y; pre[y]=x;
	pre[x]=z;
	if(z) ch[z][ch[z][1]==y]=x;
	Push_Up(y);
}
inline void zigzig(int x){
	int y=pre[x], z=pre[y], fz=pre[z];
	ch[z][1]=ch[y][0]; pre[ch[y][0]]=z;
	ch[y][1]=ch[x][0]; pre[ch[x][0]]=y;
	pre[z]=y; ch[y][0]=z;
	pre[y]=x; ch[x][0]=y;
	pre[x]=fz;
	if(fz) ch[fz][ch[fz][1]==z]=x;
	Push_Up(z); Push_Up(y);
}
inline void zagzag(int x){
	int y=pre[x], z=pre[y], fz=pre[z];
	ch[z][0]=ch[y][1]; pre[ch[y][1]]=z;
	ch[y][0]=ch[x][1]; pre[ch[x][1]]=y;
	pre[z]=y; ch[y][1]=z;
	pre[y]=x; ch[x][1]=y;
	pre[x]=fz;
	if(fz) ch[fz][ch[fz][1]==z]=x;
	Push_Up(z); Push_Up(y);
}
inline void zigzag(int x){
	int y=pre[x], z=pre[y], fz=pre[z];
	ch[y][1]=ch[x][0]; pre[ch[x][0]]=y;
	ch[z][0]=ch[x][1]; pre[ch[x][1]]=z;
	pre[y]=pre[z]=x;
	ch[x][0]=y; ch[x][1]=z;
	pre[x]=fz;
	if(fz) ch[fz][ch[fz][1]==z]=x;
	Push_Up(z); Push_Up(y);
}
inline void zagzig(int x){
	int y=pre[x], z=pre[y], fz=pre[z];
	ch[y][0]=ch[x][1]; pre[ch[x][1]]=y;
	ch[z][1]=ch[x][0]; pre[ch[x][0]]=z;
	pre[y]=pre[z]=x;
	ch[x][1]=y; ch[x][0]=z;
	pre[x]=fz;
	if(fz) ch[fz][ch[fz][1]==z]=x;
	Push_Up(z); Push_Up(y);
}
void Splay(int x, int goal){
	int y, z;
	Push_Down(x);
	while(pre[x]!=goal){
		if(pre[pre[x]]==goal){
			y=pre[x]; Push_Down(y); Push_Down(x);
			if(ch[y][1]==x) zig(x);
			else zag(x);
		}
		else{
			y=pre[x]; z=pre[y];
			Push_Down(z); Push_Down(y); Push_Down(x);
			if(ch[z][1]==y){
				if(ch[y][1]==x) zigzig(x);
				else zagzig(x);
			}
			else{
				if(ch[y][0]==x) zagzag(x);
				else zigzag(x);
			}
		}
	}
	Push_Up(x);
	if(pre[x]==0) root=x;
}
//Splay调整,将根为r的子树调整为goal
/*inline void Splay(int r,int goal){
	Push_Down(r);
	while(pre[r]!=goal){
		//父节点即是目标位置,goal为0表示,父节点就是根结点
		if(pre[pre[r]]==goal)
			Rotate(r,ch[pre[r]][0]==r);
		else{
			int y=pre[r];
			int kind=(ch[pre[y]][0]==y);
			//两个方向不同,则先左旋再右旋
			if(ch[y][kind]==r){
				Rotate(r,!kind);
				Rotate(r,kind);
			}
			//两个方向相同,相同方向连续两次
			else{
				Rotate(y,kind);
				Rotate(r,kind);
			}
		}
	}
	Push_Up(r);
	//更新根结点
	if(goal==0) root=r;
}
*/
inline int Get_Max(int r){  
	Push_Down(r);
	while(ch[r][1]){
		r=ch[r][1];
		Push_Down(r); 
	}
	return r;  
}  
inline void Remove(){ // erase the root
	if(ch[root][0]==0){
		root=ch[root][1];
		pre[root]=0;
	}
	else{
		int m=Get_Max(ch[root][0]);
			Splay(m,root);
		ch[m][1]=ch[root][1];
		pre[ch[root][1]]=m;
		root=m;
		pre[root]=0;
		Push_Up(root);
	}
}
inline void BulidTree(int &x,int l,int r,int father){
	if(l>r)
		return;
	int mid=(l+r)>>1;
	NewNode(x,father,mid);
	BulidTree(ch[x][0],l,mid-1,x);
	BulidTree(ch[x][1],mid+1,r,x);
	Push_Up(x);
}
 
struct Node{
	int idx,num;
}a[N];
inline bool cmp(Node n1,Node n2){
	return n1.num!=n2.num?n1.num<n2.num:n1.idx<n2.idx;
}
inline void Init(){
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i].num);
		a[i].idx=i;
	}
	sort(a+1,a+1+n,cmp);
	ch[0][0]=ch[0][1]=pre[0]=size[0]=rev[0]=0;
	root=tot1=0;
	BulidTree(root,1,n,0); 
}
int main(){
	while(scanf("%d",&n)!=EOF&&n){
		Init();
		for(int i=1;i<n;i++){		
			Splay(a[i].idx,0);
			rev[ch[root][0]]^=1;			
			printf("%d ",i+size[ch[root][0]]);			
			Remove();			
		}
		printf("%d\n",n);
	}
	return 0;
}


### HDU4453 Splay 问题的C++实现及其时间复杂度分析 HDU4453 是一个涉及Splay Tree(伸展树)的问题,要求对一系列操作进行高效处理。以下是对该问题的时间复杂度分析以及C++实现。 #### 1. Splay Tree 的基本操作时间复杂度 Splay Tree 是一种自调整二叉搜索树,其核心思想是通过旋转操作将访问过的节点移动到根节点位置,从而优化后续访问性能。Splay Tree 的基本操作包括插入、删除、查找和分裂等,这些操作的时间复杂度均为 \(O(\log n)\) 在均摊意义上[^1]。 - **插入操作**:在Splay Tree 中插入一个新节点时,首先需要找到插入位置,这一步的时间复杂度为 \(O(\log n)\)。插入后,树会通过一系列旋转操作将新节点调整到根节点位置。 - **删除操作**:删除操作需要先找到目标节点并将其旋转到根节点位置,然后调整树结构以移除该节点。这一过程的时间复杂度同样为 \(O(\log n)\)。 - **查找操作**:查找操作需要从根节点开始向下遍历,直到找到目标节点或确定目标节点不存在。查找完成后,目标节点会被旋转到根节点位置。查找操作的时间复杂度为 \(O(\log n)\)。 #### 2. HDU4453 的具体问题分析 HDU4453 要求对一个序列执行多种操作,包括插入、删除、查询排名、查询值等。以下是针对这些操作的时间复杂度分析: - **插入操作**:每次插入一个新元素时,Splay Tree 需要调整树结构以保持平衡。由于 Splay Tree 的插入操作时间复杂度为 \(O(\log n)\),因此对于 \(m\) 次插入操作,总时间复杂度为 \(O(m \log n)\)[^1]。 - **删除操作**:删除操作与插入操作类似,时间复杂度为 \(O(\log n)\)。对于 \(m\) 次删除操作,总时间复杂度为 \(O(m \log n)\)[^1]。 - **查询排名**:查询某个值的排名需要从根节点开始向下遍历,并根据左子树的大小计算排名。这一过程的时间复杂度为 \(O(\log n)\)[^1]。 - **查询值**:查询排名对应的值也需要从根节点开始向下遍历,时间复杂度同样为 \(O(\log n)\)。 #### 3. C++ 实现示例 以下是一个基于 Splay Tree 的 C++ 实现示例: ```cpp #include <bits/stdc++.h> using namespace std; struct Node { int val, size; Node *left, *right, *parent; Node(int v) : val(v), size(1), left(nullptr), right(nullptr), parent(nullptr) {} }; int getSize(Node* node) { return node ? node->size : 0; } void updateSize(Node* node) { if (node) node->size = 1 + getSize(node->left) + getSize(node->right); } void rotateRight(Node* &root, Node* y) { Node* x = y->left; y->left = x->right; if (x->right) x->right->parent = y; x->right = y; x->parent = y->parent; y->parent = x; if (x->parent) { if (x->parent->left == y) x->parent->left = x; else x->parent->right = x; } updateSize(y); updateSize(x); root = x; } void rotateLeft(Node* &root, Node* x) { Node* y = x->right; x->right = y->left; if (y->left) y->left->parent = x; y->left = x; y->parent = x->parent; x->parent = y; if (y->parent) { if (y->parent->left == x) y->parent->left = y; else y->parent->right = y; } updateSize(x); updateSize(y); root = y; } void splay(Node* &root, Node* x) { while (x->parent) { Node* p = x->parent; if (!p->parent) { if (p->left == x) rotateRight(root, p); else rotateLeft(root, p); } else { Node* pp = p->parent; if (pp->left == p) { if (p->left == x) { rotateRight(root, p); rotateRight(root, pp); } else { rotateLeft(root, x); rotateRight(root, pp); } } else { if (p->right == x) { rotateLeft(root, p); rotateLeft(root, pp); } else { rotateRight(root, x); rotateLeft(root, pp); } } } } root = x; } Node* findKth(Node* root, int k) { while (true) { int leftSize = getSize(root->left); if (k <= leftSize) { root = root->left; } else if (k == leftSize + 1) { return root; } else { k -= leftSize + 1; root = root->right; } } } Node* insert(Node* &root, int key) { if (!root) return root = new Node(key); Node* cur = root; while (true) { if (key < cur->val) { if (!cur->left) { cur->left = new Node(key); cur->left->parent = cur; break; } cur = cur->left; } else { if (!cur->right) { cur->right = new Node(key); cur->right->parent = cur; break; } cur = cur->right; } } updateSize(root); Node* newNode = key < cur->val ? cur->left : cur->right; splay(root, newNode); return root; } Node* remove(Node* &root, int key) { if (!root) return nullptr; Node* cur = root; while (cur) { if (key == cur->val) break; cur = key < cur->val ? cur->left : cur->right; } if (!cur) return root; splay(root, cur); if (!cur->left && !cur->right) { delete cur; root = nullptr; } else if (!cur->left) { root = cur->right; cur->right->parent = nullptr; delete cur; } else if (!cur->right) { root = cur->left; cur->left->parent = nullptr; delete cur; } else { Node* maxLeft = cur->left; while (maxLeft->right) maxLeft = maxLeft->right; splay(root, maxLeft); maxLeft->right = cur->right; cur->right->parent = maxLeft; delete cur; } updateSize(root); return root; } ``` #### 4. 总结 在 HDU4453 中,使用 Splay Tree 可以高效地完成插入、删除、查询排名和查询值等操作。所有这些操作的时间复杂度均为 \(O(\log n)\) 在均摊意义上[^1]。因此,对于 \(m\) 次操作,总时间复杂度为 \(O(m \log n)\)。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值