hdu1890 Robotic Sort splay+懒惰标记+翻转

题意:给定一个序列,需要我们将这个序列最终排成单调增的序列。每次操作,你需要找到最小的值(如果有多个找最前面的那个)

将未排序的起始段到这个最小值的位置之间的区间进行翻转。对每次操作输出最小值的位置。

思路:splay。我们用splay维护未排序的序列。初始值记录在a数组中,splay的val存这个结点在a数组的位置,dir存当前为排序的序

列中最小值在当前根的左端还是右端,pos记录以当前结点为根的子树中最小值的位置。每次将最小值的后继旋转到根,统计左子树

的结点个数,然后加上之前已经排序的个数-1就是这个最小值的位置。为了方便边界的处理,我们新建两个结点root,R(root),root一定

在splay树的最左端,R(root)一定在最右端。详见代码:

// file name: hdu1890.cpp //
// author: kereo //
// create time:  2014年08月30日 星期六 12时05分17秒 //
//***********************************//
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<stack>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=100000+100;
const int inf=0x3fffffff;
#define L(x) (x<<1)
#define R(x) (x<<1|1)
int n,top,cnt;
int a[MAXN],st[MAXN];
struct node
{
	int val,s,flip,dir,pos; //val存在a数组的位置,dir存最小值在当前根的左端还是右端,pos记录以当前结点为根的子树中最小值的位置
	node *fa,*ch[2];
}nod[MAXN],nil,*root,*null;
struct Splay
{
	void init(){
		top=cnt=0;
		nil.s=nil.val=nil.flip=nil.dir=0; null=&nil; null->ch[0]=null->ch[1]=null;
		newnode(root,null,0);
		newnode(root->ch[1],root,0);
		push_up(root);	a[0]=inf;
		build(root->ch[1]->ch[0],root->ch[1],1,n);
		push_up(root->ch[1]); push_up(root);
	}
	void newnode(node *&x,node *f,int val){
		if(top) x=&nod[st[--top]];
		else x=&nod[cnt++];
		x->s=1; x->val=x->pos=val; x->flip=x->dir=0; x->fa=f; x->ch[0]=x->ch[1]=null;
	}
	void Delete(node *x,node *f){
		if(x ==null) return;
		f->ch[0]=null; x->fa=null;
		f->pos=f->val; f->dir=0;
		int r=f->ch[1] == null ? 0 : f->ch[1]->pos;
		if(a[r]<a[f->pos] || a[r] == a[f->pos] && r<f->pos)
			f->pos=r,f->dir=1;
		st[top++]=x-nod;
	}
	void push_up(node *x){
		x->s=1;
		if(x->ch[0]!=null) x->s+=x->ch[0]->s;
		if(x->ch[1]!=null) x->s+=x->ch[1]->s;
		x->pos=x->val; x->dir=0;
		int l=x->ch[0] == null ? 0 : x->ch[0]->pos;
		int r=x->ch[1] == null ? 0 : x->ch[1]->pos;
		if(a[l]<a[x->pos] || a[l] == a[x->pos] && l<x->pos)
			x->pos=l,x->dir=-1;
		if(a[r]<a[x->pos] || a[r] == a[x->pos] && r<x->pos)
			x->pos=r,x->dir=1;
	}
	void push_down(node *x){
		if(x->flip){
			x->flip^=1;
			if(x->ch[0]!=null) x->ch[0]->flip^=1;
			if(x->ch[1]!=null) x->ch[1]->flip^=1;
			swap(x->ch[0],x->ch[1]);
			x->dir*=-1;
		}
	}
	void rotate(node *x,int d){
		node *y=x->fa;
		push_down(y); push_down(x);
		y->ch[d^1]=x->ch[d];
		if(x->ch[d]!=null) x->ch[d]->fa=y;
		x->fa=y->fa;
		if(y->fa!=null){
			int d1=y->fa->ch[0] == y ? 0 : 1;
			y->fa->ch[d1]=x;
		}
		x->ch[d]=y; y->fa=x;
		push_up(y);
	}
	void splay(node *x,node *f){
		while(x->fa!=f){
			push_down(x);
			node *y=x->fa;
			if(y->fa == f){
				int d=y->ch[0] == x ? 1 : 0;
				rotate(x,d);
			}
			else{
				int d=y->fa->ch[0] == y ? 1: 0;
				if(y->ch[d] == x){
					rotate(x,d^1); rotate(x,d);
				}
				else{
					rotate(y,d); rotate(x,d);
				}
			}
		}
		push_up(x);
		if(f == null) root=x;
	}
	node *get_prefix(node *x){  //获得前驱
		node *y=x->ch[0];
		push_down(x); push_down(y);
		while(y->ch[1]!=null){
			y=y->ch[1]; push_down(y);
		}
		return y;
	}
	node *get_suffix(node *x){ //获得后继
		node *y=x->ch[1];
		push_down(x); push_down(y);
		while(y->ch[0]!=null){
			y=y->ch[0]; push_down(y);
		}
		return y;
	}
	void build(node *&x,node *f,int l,int r){
		if(l>r) return ;
		int mid=(l+r)>>1;
		newnode(x,f,mid);
		build(x->ch[0],x,l,mid-1); build(x->ch[1],x,mid+1,r);
		push_up(x);
	}
	void update(int m){
		splay(&nod[0],null); 
		node *x=root;
		while(x->dir){
			push_down(x);
			if(x->dir == -1) x=x->ch[0];
			else x=x->ch[1];
		}
		splay(x,root);
		node *p1,*p2;
		p2=get_suffix(x); 
		splay(p2,root); 
		int num=root->ch[1]->ch[0]->s;
		num+=m;
		printf("%d",num);
		x->flip=1;
		splay(x,null); 
		p1=get_prefix(x); 
		p2=get_suffix(x); 
		splay(p1,null); 
		splay(p2,root); 
		Delete(x,p2);
	}
}spt;
int main()
{
	while(~scanf("%d",&n) && n){
		for(int i=1;i<=n;i++)
			scanf("%d",&a[i]);
		spt.init();
		for(int i=0;i<n;i++){
			spt.update(i);
			if(i<n-1)
				printf(" ");
		}
		printf("\n");
		
	}
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值