uva 11922 (伸展树模板题)

给你一个长度为n 的序列(初始值为1到n)。有m 次操作,每次操作有l  r 你需要将l 到 r 的数字翻转。然后放到最后

m次操作后输出答案。

代码:

#include<bits/stdc++.h>

using namespace std;
const int inf =0x3f3f3f3f; 

struct Node
{
	Node *ch[2];
	int s,v,flip; // s孩子个数,v 键值 flip 翻转标记 
	int cmp(int k){
		int d=k-ch[0]->s;
		if(d==1) return -1; // 该节点就是要找的第k小的节点 
		return d<=0?0:1;   // 确定第k小的节点是在左子树还是右子树 
	}
	void maintain()
	{
		s=ch[0]->s+ch[1]->s+1;   // 也就相当于push_up 更新孩子的个数(包括自己) 
	}
	
	void push_down()
	{
		if(flip){
			flip=0;
			swap(ch[0],ch[1]);
			ch[0]->flip=!(ch[0]->flip);
			ch[1]->flip=!(ch[1]->flip);
		}
	}
	
};

Node *null= new Node(); // 建立一个空的节点 避免 NULL指针的错误。也可以有其他的写法 
// 每个叶节点的两个孩纸指针都会连向该节点。可以理解为 访问到叶节点的标志 

void rotate(Node* &o,int d)  // d 为0表示左旋,为1表示右旋 
{
	Node* k=o->ch[d^1];
	o->ch[d^1]=k->ch[d];
	k->ch[d]=o;
	o->maintain();
	k->maintain();
	o=k;
}

void splay(Node* &o,int k)  // 找到第k小的并进行旋转操作  使得该节点旋转到根 
{
	o->push_down();  // 如果有翻转标记 先下放标记 
	int d=o->cmp(k);  //比较在那棵子树中  -1 表示该节点 0 表示左子树 1 表示右子树 
	if(d==1) k-=(o->ch[0]->s+1); // 右子树中 
	if(d!=-1){
		Node *p=o->ch[d];
		p->push_down();
		int d2=p->cmp(k);
		int k2=(d2==0?k:k-1-p->ch[0]->s);
		if(d2!=-1){
			splay(p->ch[d2],k2);
			if(d==d2) rotate(o,d^1);
			else rotate(o->ch[d],d);
		}
		rotate(o,d^1);
	}
}

Node* merge(Node *left,Node *right) //left!=null  合并操作 
{
	splay(left,left->s);  //找到最大的  那么右子树一定为空 
	left->ch[1]=right;  // right 赋给右子树 
	left->maintain(); //push_up 
	return left;
}

void split(Node* o,int k,Node* &left,Node* &right)  //分离操作 
{
	splay(o,k);// 找到第k小的   分离操作 左子树不能为空!!! 
	left=o;  //分离 
	right=o->ch[1];  //分离 
	o->ch[1]=null;
	left->maintain();
}

const int maxn=100000+1000;
struct SplaySequence
{
	int n;
	Node seq[maxn];  //  maxn 个节点 
	Node *root;
	Node* build(int sz)
	{
		if(sz==0) return null;
		Node* L=build(sz/2);
		Node* o=&seq[++n];
		o->v=n;
		o->ch[0]=L;
		o->ch[1]=build(sz-sz/2-1);
		o->s=o->flip=0;
		o->maintain();
		return o;
	}
	
	void init(int sz)
	{
		n=0;
		null->s=0;
		root=build(sz);
	}
};


vector< int >ans;

void print(Node *o)
{
	if(o!=null){
		o->push_down();
		print(o->ch[0]);
		ans.push_back(o->v);
		print(o->ch[1]);
	}
}

void debug(Node* o)
{
	if(o!=null){
		o->push_down();
		debug(o->ch[0]);
		printf("%d ",o->v-1);
		debug(o->ch[1]);
	}
}

SplaySequence ss;

// 分离操作,左子树不能为空,所以对于该题来说,我们可以建立一棵
// 叶节点为n+1 的树 ,这样 -1 就是我们要的答案。 

int main()
{
	int n,m;
	scanf("%d %d",&n,&m);
	ss.init(n+1);
	
	while(m--)
	{
		int a,b;
		scanf("%d %d",&a,&b);
		Node *o,*mid,*left,*right;
		split(ss.root,a,left,o);
		split(o,b-a+1,mid,right);
		mid->flip^=1;
		ss.root=merge(merge(left,right),mid);
	}
	print(ss.root);
	for(int i=1;i<ans.size();i++){
		printf("%d\n",ans[i]-1);
	}
	
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值