hdu3487 Play with Chain(Splay)

2 篇文章 0 订阅
1 篇文章 0 订阅

Play with Chain

Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 2761    Accepted Submission(s): 1136


Problem Description
YaoYao is fond of playing his chains. He has a chain containing n diamonds on it. Diamonds are numbered from 1 to n.
At first, the diamonds on the chain is a sequence: 1, 2, 3, …, n.
He will perform two types of operations:
CUT a b c: He will first cut down the chain from the ath diamond to the bth diamond. And then insert it after the cth diamond on the remaining chain.
For example, if n=8, the chain is: 1 2 3 4 5 6 7 8; We perform “CUT 3 5 4”, Then we first cut down 3 4 5, and the remaining chain would be: 1 2 6 7 8. Then we insert “3 4 5” into the chain before 5th diamond, the chain turns out to be: 1 2 6 7 3 4 5 8.

FLIP a b: We first cut down the chain from the ath diamond to the bth diamond. Then reverse the chain and put them back to the original position.
For example, if we perform “FLIP 2 6” on the chain: 1 2 6 7 3 4 5 8. The chain will turn out to be: 1 4 3 7 6 2 5 8

He wants to know what the chain looks like after perform m operations. Could you help him? 
 

Input
There will be multiple test cases in a test data. 
For each test case, the first line contains two numbers: n and m (1≤n, m≤3*100000), indicating the total number of diamonds on the chain and the number of operations respectively.
Then m lines follow, each line contains one operation. The command is like this:
CUT a b c // Means a CUT operation, 1 ≤ a ≤ b ≤ n, 0≤ c ≤ n-(b-a+1).
FLIP a b    // Means a FLIP operation, 1 ≤ a < b ≤ n.
The input ends up with two negative numbers, which should not be processed as a case.
 

Output
For each test case, you should print a line with n numbers. The ith number is the number of the ith diamond on the chain.
 

Sample Input
  
  
8 2 CUT 3 5 4 FLIP 2 6 -1 -1
 

Sample Output
  
  
1 4 3 7 6 2 5 8
 
 题意:一列树,执行截断插入或者区间调换
 此题是我人生第一次做splay,虽然比较简单,但是的确花了不少时间再理解方面。。。。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define KeyNode (ch[ch[rt][1]][0])
#define N 300005
#define MOV(a,b) RotateTo(a-1,0);RotateTo(b+1,rt);
int pre[N],val[N],ch[N][2],size[N],mark[N],rt,id;
inline void push_up(int x){//自下而上
	size[x]=1+size[ch[x][0]]+size[ch[x][1]];//注意这个1
}
inline void push_down(int x){//自上而下
	if(mark[x]){
		swap(ch[x][0],ch[x][1]);
		mark[ch[x][0]]^=1;
		mark[ch[x][1]]^=1;
		mark[x]=0;
	}
}
void Rotate(int x,int f){
	int y=pre[x];
	push_down(y);
	push_down(x);
	ch[y][!f]=ch[x][f];
	pre[ch[x][f]]=y;
	if(pre[y]){
		if(ch[pre[y]][0]==y)ch[pre[y]][0]=x;
		else ch[pre[y]][1]=x;
	}
	pre[x]=pre[y];
	ch[x][f]=y;
	pre[y]=x;
	push_up(y);//为什么要这样push_up???
    //因为每一次Rotate都把原来x的父节点给调到了x的下面,所以结束的时候先维护处于下面的y结点
}
void Splay(int x,int goal){
	//push_down(x);RotateTo已经push_down过了
	while(pre[x]!=goal){
		int y=pre[x],z=pre[y];
		//pre数组是会变的,所以这个赋值语句千万不能写到外面去
		if(pre[pre[x]]==goal)
			Rotate(x,ch[pre[x]][0]==x);
		else{
			int f=(ch[z][0]==y);
			if(ch[y][f]==x)//之字形旋转
				Rotate(x,!f),Rotate(x,f);
			else//一字型旋转
				Rotate(y,f),Rotate(x,f);
		}
	}
	if(goal==0)rt=x;
	push_up(x);//因为经过splay最上面的是x结点,所以x最后pushup
}
void RotateTo(int k,int goal){
	int x=rt;
	push_down(x);
	while(size[ch[x][0]]!=k){
		//因为多插入了一大一小的结点,所以找第k个数相当于找第k+1个数。即左子树=k的数
		//其实是要从小到大,依照中序遍历的规律,先向左走完再输出,往右走的话即代表前面都比他小了。
		if(size[ch[x][0]]>k)x=ch[x][0];
		else{
			k-=(size[ch[x][0]]+1);
			x=ch[x][1];
		}
		push_down(x);
	}
	Splay(x,goal);
}
void NewNode(int &x,int c,int fa){
	x=++id;
	ch[x][0]=ch[x][1]=0;
	pre[x]=fa;val[x]=c;size[x]=1;mark[x]=0;
}
void Build(int &x,int l,int r,int fa){
	if(l>r)return;
	int m=(l+r)>>1;
	NewNode(x,m,fa);
	Build(ch[x][0],l,m-1,x);
	Build(ch[x][1],m+1,r,x);
	push_up(x);
}//以先序遍历的形式建立val的中序遍历
void init(int n){
	rt=id=0;
	NewNode(rt,0,0);
	NewNode(ch[rt][1],0,rt);
	//将val=0的结点挂到val=0的右边,其实这里就违反了二叉搜索树的性质,
	//所以这个0应该看成是一个大于所有结点的最大的数,其他的树其实是夹在这两个数之间的
	Build(KeyNode,1,n,ch[rt][1]);
	push_up(ch[rt][1]);
	push_up(rt);
}
int n,m,tot;
void Tprint(int x){
	if(x){
		push_down(x);
		Tprint(ch[x][0]);
		printf("%d%c",val[x],++tot==n?'\n':' ');
		Tprint(ch[x][1]);
	}
}
//void Treaval(int x) {
//		if(x) {
//			Treaval(ch[x][0]);
//			printf("结点%2d:左儿子 %2d 右儿子 %2d 父结点 %2d size = %2d ,val = %2d\n",x,ch[x][0],ch[x][1],pre[x],size[x],val[x]);
//			Treaval(ch[x][1]);
//		}
//	}
//void debug() {printf("%d\n",rt);Treaval(rt);}
int main(int argc, char **argv) {
	while(scanf("%d%d",&n,&m),m>=0&&n>=0){
		int a,b,c;
		char opt[10];
		init(n);
		//debug();
		//RotateTo(10,0);
		//debug();
		while(m--){
			scanf("%s",opt);
			if(opt[0]=='C'){
				scanf("%d%d%d",&a,&b,&c);
				MOV(a,b);
				//debug();
				int CR=KeyNode;
				KeyNode=0;
				MOV(c+1,c);
				KeyNode=CR;
				pre[KeyNode]=ch[rt][1];
			}
			else{
				scanf("%d%d",&a,&b);
				MOV(a,b);
				mark[KeyNode]^=1;
			}
		}
		tot=0;
		MOV(1,n);
		Tprint(KeyNode);
	}
	return 0;
}

除此之外,之前还写了一个,用struct写的
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 300005
bool first;
#define KeyNode (spt.ch[spt.ch[spt.root][1]][0])
struct SplayTree{
       int sz[MAXN];             //sz[x]-x为根结点的子树的所有结点数
       int ch[MAXN][2];          //ch[x][0]-x的左子节点;ch[x][1]-x的右子节点
       int pre[MAXN];            //pre[x]-x的父结点
       int val[MAXN];
       int mak[MAXN];
       int root,id;
       inline void push_up(int x);
       inline void push_down(int x);
       inline void Rotate(int x,int f);
       inline void Splay(int x,int goal);
       inline void RotateTo(int k,int goal);
       inline void NewNode(int &x,int c,int fa);
       inline void Build(int &x,int l,int r,int fa);
       inline void init(int n);
       inline void OutTree(int x);
}spt;
inline void SplayTree::Rotate(int x,int f){//f:0是左旋(向左旋转!!),1是右旋(向右旋转!!) 切记!!!
	int y=pre[x];
	push_down(y);
	push_down(x);
	ch[y][!f]=ch[x][f];
	pre[ch[x][f]]=y;
	pre[x]=pre[y];
	if(pre[y]){
		if(ch[pre[y]][0]==y)ch[pre[y]][0]=x;
		else ch[pre[y]][1]=x;
	}
	ch[x][f]=y;
	pre[y]=x;
	push_up(y);
}
inline void SplayTree::Splay(int x,int goal){
	push_down(x);
	while(pre[x]!=goal){
		int y=pre[x],z=pre[y];
		if(pre[pre[x]]==goal)
			Rotate(x,ch[pre[x]][0]==x);
		else{
			int f=(ch[z][0]==y);
			if(ch[y][f]==x){//之字型旋转
				Rotate(x,!f);
				Rotate(x,f);
			}else{//一字型旋转
				Rotate(y,f);
				Rotate(x,f);
			}
		}
	}
	push_up(x);
	if(goal==0)root=x;
}
inline void SplayTree::RotateTo(int k,int goal){
	int x=root;
	push_down(x);
	while(sz[ch[x][0]]!=k){
		if(sz[ch[x][0]]>k)x=ch[x][0];
		else{
			k-=(sz[ch[x][0]]+1);
			x=ch[x][1];
		}
		push_down(x);
	}
	Splay(x,goal);
}
//==================================以上是不变的=============================================
inline void SplayTree::push_up(int x){
	sz[x]=1+sz[ch[x][0]]+sz[ch[x][1]];
}
inline void SplayTree::push_down(int x){
	if(mak[x]){
		swap(ch[x][0],ch[x][1]);
		mak[ch[x][0]]^=1;
		mak[ch[x][1]]^=1;
		mak[x]=0;
	}
}
inline void SplayTree::NewNode(int &x,int c,int fa){
	x=++id;
	ch[x][0]=ch[x][1]=0;
	pre[x]=fa;val[x]=c;
	mak[x]=0;sz[x]=1;
}
inline void SplayTree::Build(int &x,int l,int r,int fa){
	if(l>r)return;
	int mid=(l+r)>>1;
	NewNode(x,mid,fa);
	Build(ch[x][0],l,mid-1,x);
	Build(ch[x][1],mid+1,r,x);
	push_up(x);
}
inline void SplayTree::init(int n){
	root=id=0;
	NewNode(root,0,0);
	NewNode(ch[root][1],0,root);
	Build(KeyNode,1,n,ch[root][1]);
	push_up(ch[root][1]);
	push_up(root);
}
inline void SplayTree::OutTree(int x){
	if(x==0)return;
	push_down(x);
	OutTree(ch[x][0]);
	if(first)printf("%d",val[x]);
	else printf(" %d",val[x]);
	first=false;
	OutTree(ch[x][1]);
}
int main(){
	int n,m,a,b;
	char opt[5];
	while(scanf("%d%d",&n,&m),n>=0&&m>=0){
		spt.init(n);
		while(m--){
			scanf("%s%d%d",opt,&a,&b);
			if(opt[0]=='C'){
				int c;
				scanf("%d",&c);
				spt.RotateTo(a-1,0);
				spt.RotateTo(b+1,spt.root);
				int CutRoot=KeyNode;
				KeyNode=0;
				spt.push_down(spt.ch[spt.root][1]);
				spt.push_down(spt.root);

				spt.RotateTo(c,0);
				spt.RotateTo(c+1,spt.root);
				KeyNode=CutRoot;

				spt.pre[KeyNode]=spt.ch[spt.root][1];
				spt.push_down(spt.ch[spt.root][1]);
				spt.push_down(spt.root);
			}
			else{
				spt.RotateTo(a-1,0);
				spt.RotateTo(b+1,spt.root);
				spt.mak[KeyNode]^=1;
				spt.push_down(spt.ch[spt.root][1]);
				spt.push_down(spt.root);
			}
		}
		spt.RotateTo(0,0);
		spt.RotateTo(n+1,spt.root);
		first=true;
		spt.OutTree(KeyNode);
		printf("\n");
	}
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值