HDU 3487 splay区间维护问题

题目意思:

给定一个1,2,3,4...n的序列


操作1:

把第L到R个元素切掉,然后插入新的序列第k个元素后面


操作2:

第L个,到第R个元素之间的所有元素进行倒序。


splay模板:


这里我自己做了自己的splay模板,太慢!勉强能用!


#include <cstdio>
#include <vector>
#include <cstdlib>
#include <iostream>
#include <cmath>
using namespace std;

const int maxint=~0U>>1;

/*
splay,各个节点之间需要传递的信息
*/

struct mark
{
	bool reverse; //这个节点的所有子节点是否要颠倒
	/*
	bool b;
	bool a;
	表示a,b表示一些需要传递的参数,比如是否要倒置之类,或者区间加上一直值,减去一个值
	*/
	mark()
	{
		reverse=0;
	}
	//1要颠倒   0不要颠倒
};
vector<int>outputnum;

/*
splay的节点信息
*/
struct node
{
	int key;
	int size;
	mark cd;

	node *c[2];
	node():key(0),size(0){c[0]=c[1]=this;}
	node(int key_,node* c0_,node* c1_):
	key(key_){c[0]=c0_;c[1]=c1_;}
	node* rz(){return size=c[0]->size+c[1]->size+1,this;}
} Tnull,*null=&Tnull;

struct splay
{
	node *root;
	splay() /*初始化  不需要做任何修改*/
	{
		root=(new node(*null))->rz();
		root->key=maxint;
	}	



	/*把k节点的信息,传递到k节点的两个儿子,同时,如果是逆转操作,左右儿子
	 * 需要进行交换位置。  【只传递给两个儿子!】
	 * do_reversal有自动判断,当前node是否有信息要传递的功能
	*/
	void do_reversal(node* k)
	{
		if (k -> cd.reverse)//如果这个节点需要传递
		{
			k -> cd.reverse = 0; //标记为已经传递,标记下传,并且交换
			swap(k -> c[0], k -> c[1]);//左右儿子交换
			if (k -> c[0] != null)	k -> c[0] -> cd.reverse ^= 1;
			if (k -> c[1] != null) 	k -> c[1] -> cd.reverse ^= 1;
		}
	}


	/*
	 * 从根节点把信息,向下传递2层
	*/
	void check()
	{
		do_reversal(root);//根的左右儿子逆转,先把根的信息处理完,do_reversal会自动判断当前节点是否要做操作
		//然后判断,根的左右儿子是否有信息要传递
		if (root -> c[0] != null)	do_reversal(root -> c[0]);
		if (root -> c[1] != null)	do_reversal(root -> c[1]);
	}

	//zig,zigzig,finish,select操作,全部和key无关,不需要做任何修改
	void zig(bool d)
	{
		node *t=root->c[d];
		root->c[d]=null->c[d];
		null->c[d]=root;
		root=t;
	} 
	void zigzig(bool d)
	{
		node *t=root->c[d]->c[d];
		root->c[d]->c[d]=null->c[d];
		null->c[d]=root->c[d];
		root->c[d]=null->c[d]->c[!d];
		null->c[d]->c[!d]=root->rz();
		root=t;
	}
	void finish(bool d)
	{
		node *t=null->c[d],*p=root->c[!d];
		while(t!=null)
		{
			t=null->c[d]->c[d];
			null->c[d]->c[d]=p;
			p=null->c[d]->rz();
			null->c[d]=t;
		}
		root->c[!d]=p;
	}
	void select(int k) //第k+1个元素旋转到根,也就是有k个数字比要找的数字小
	{
		int t;
		while(1)
		{
			check();
			bool d=k>(t=root->c[0]->size);
			if(k==t||root->c[d]==null)break;
			if(d)k-=t+1;
			bool dd=k>(t=root->c[d]->c[0]->size);
			if(k==t||root->c[d]->c[dd]==null){zig(d);break;}
			if(dd)k-=t+1;
			d!=dd?zig(d),zig(dd):zigzig(d);
		}
		finish(0),finish(1);
		root->rz();//更新根节点的size
	}




	/*
		pg(node k);
		打印整棵树,包括左右儿子信息,size,传递的参数
		通常直接调用 pg(*root) 即可
	*/
	void pg(node k)//打印这棵树

	{
		cout<<k.key<<" [left son:";
		if (k.c[0] != null)	cout<<k.c[0] -> key;
		else cout<<"none";
		cout<<"]  [right son:";

		if (k.c[1] != null)	cout<< k.c[1] -> key;
		else cout<<"none";
		cout<<"]     [size =";
		cout<<k.size<<"]" <<"逆转否?"<<" "<<k.cd.reverse<<"\n";
		if (k.c[0] != null)	pg(*k.c[0]);
		if (k.c[1] != null)	pg(*k.c[1]);
	}



	/*
	 * 输出这棵树的中序遍历,包括-maxint,和+maxin
	 * 调用方式通常直接为zhongxu(root)
	*/
	void zhongxu(node* k)
	{
		do_reversal(k);//根的左右儿子逆转
		if (k -> c[0] != null)	do_reversal(k -> c[0]);
		if (k -> c[1] != null)	do_reversal(k -> c[1]);
		if (k -> c[0] != null)	zhongxu(k -> c[0]);
		cout<<k -> key<<" ";
		if (k -> c[1] != null)	zhongxu(k -> c[1]);			
	}


	//	vector<int>outputnum;必须定义
	//	必须在调用整个函数之前初始化outputnum
	void get_output_info(node* k)
	{
		do_reversal(k);//根的左右儿子逆转
		if (k -> c[0] != null)	do_reversal(k -> c[0]);
		if (k -> c[1] != null)	do_reversal(k -> c[1]);
		if (k -> c[0] != null)	get_output_info(k -> c[0]);
		outputnum.push_back(k -> key);
		if (k -> c[1] != null)	get_output_info(k -> c[1]);			
	}
	void output()
	{
		outputnum.clear();
		get_output_info(root);
		for (int i = 1; i != outputnum.size() - 2; ++ i)	printf("%d ", outputnum[i]);
		printf("%d\n", outputnum[outputnum.size() - 2]);
	}
	///



	node* init(int x[], int n)//形成一颗初始树, 有n个元素,下标从1开始的数组
	{
		//因为是初始化,所以先添加了一个最小的节点-maxint进这棵树
		root -> c[0] = (new node(-maxint, null, null)) -> rz();//左儿子插入一个新元素
		root -> rz();
		for (int i = 1; i <= n; ++ i)
		{
			transformation(i, i + 1);
			root -> c[1] -> c[0] = (new node(x[i], null, null)) -> rz();
			root -> c[1] -> rz();
			root -> rz();
		}
		return root;
	}
	node* init_no_maxint(int x[], int n)//同上,但是不添加-maxint,和max进这棵树,这是为了方便合并
	{
		root -> c[0] = (new node(x[1], null, null)) -> rz();//左儿子插入一个新元素
		root -> rz();
		for (int i = 2; i <= n; ++ i)
		{
			transformation(i - 1, i);
			root -> c[1] -> c[0] = (new node(x[i], null, null)) -> rz();
			root -> c[1] -> rz();
			root -> rz();
		}	
		transformation(n, n + 1);
		delete root -> c[1];
		root -> c[1] = null;
		return root -> rz();
	}



	/*
	 * clear()以下为删除把整个splay清空为初始化状态
	*/
	void dfs_clear(node *k)
	{
		if (k -> c[0] != null)
		{
			dfs_clear(k -> c[0]);
			delete (k -> c[0]);
		}
		if (k -> c[1] != null)
		{
			dfs_clear(k -> c[1]);
			delete k -> c[1];
		}
	}
	node* clear() //删除整棵树,只留下一个maxint在树中
	{
		int allsize = root -> size;
		select(allsize - 1);
		dfs_clear(root);
		root -> c[0] = root -> c[1] = null;
		return root;
	}


	/*
	 * 把第left个节点旋转到根,right为根的右儿子
	 * 也就是提取区间[left+1,right-1]
	 * 当然,这里包括-maxint, maxint这2个节点
	*/
	node* transformation(int left, int right)
	{
		select(left - 1);

		node *oldroot = root;
		root = root -> c[1];

		select(right -left - 1);
		node *t = root;


		root = oldroot;
		root -> c[1] = t;
		return root -> rz();
	}


	/*
	 * 把[left+1, right -1]之间的东西倒置
	*/
	node* reversal(int left, int right) // (left,right)之间要倒置,不包含left,right
	{
		transformation(left, right);
		root -> c[1] -> c[0] -> cd.reverse ^= 1;
		return root;
	}
	int sel(int k){return select(k-1),root->key;} //第k名的是谁
} sp;


struct interval// 形成区间的一个splay
{
	splay core;//包含头一个-max,后一个max

	inline int size()
	{
		return core.root -> size - 2;
	}

	inline void output()
	{
		core.output();
	}

	inline void clear()
	{
		core.clear();
	}

	inline void init(int x[], int n)//初始化,给core里初始值
		//下标从1开始,一共n个元素!
	{
		core.init(x, n);
	}

	/*
	 * 第left个元素,到第right个元素,全部倒转
	*/
	void daozhuan(int left, int right) 
	{
		right += 2;
		core.reversal(left, right);
	}

	/*
	 * 按照顺序输出所有元素,注意,这里会让所有传递信息进行传递!
	*/
	void output_all_element()
	{
		core.zhongxu(core.root);
		cout<<endl;
	}
	

	node* delete_qujian(int left, int right)
	{
		right += 2;
		core.transformation(left, right);
		node *ret = core.root -> c[1] -> c[0];
		core.root -> c[1] -> c[0] = null;
		core.root -> c[1] -> rz();
		core.root -> rz();
		return ret;
	}

	void insert_node(node *newinter, int k)//在区间[1,k],[k+1,n]之间插入
	{
		++k; //因为存在-max的元素,所以区间都要后置一点
		core.transformation(k, k + 1);
		core.root -> c[1] -> c[0] = newinter;
	}

	/*
	 * 在第k个元素后面插入n个元素,n个元素保存在x[1..n]中。 下标为1开始
	*/
	void insert(int x[], int n, int k)
	{
		splay tmp;
		node *tmproot = tmp.init_no_maxint(x, n);
		insert_node(tmproot, k);
	}
}qujian;

int a[300000 + 100];

int main()
{
	int data, que;
	char s[100];
	while (1)
	{
		scanf("%d%d", &data, &que);
		if (data == -1 && que == -1)	break;
		qujian.clear();
		for (int i = 1; i <= data; ++ i)	a[i] = i;
		qujian.init(a, data);
		while (que--)
		{
			scanf("%s", s);
			if (s[0] == 'C')
			{
			//	cout<<"!"<<endl;
				int left, right, k;
				scanf("%d%d%d", &left, &right, &k);
				node *tmp = qujian.delete_qujian(left, right);
				qujian.insert_node(tmp, k);
			}else{
			
				int left, right;	
				scanf("%d%d", &left, &right);
				qujian.daozhuan(left, right);
			}
		}
		qujian.output();
	}
	return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值