伸展树

#include<bits/stdc++.h>
using namespace std;
#define Max 100005

int child[Max][2],fa[Max],size[Max],lazy[Max];
int n,m,root;
void updet(int x)     //更新以x为根节点树的位置; 
{
	size[x] = size[child[x][0]] + size[child[x][1]] + 1; 
}
void pushdown(int x)         // 向下传懒惰标记; 
{
	if(lazy[x])
	{
		lazy[x] ^= 1;
		lazy[child[x][0]] ^= 1;
		lazy[child[x][1]] ^= 1;
		swap(child[x][0],child[x][1]);
	}
}

int build(int left ,int right ,int f)    // 建树; 
{
	int mid = (left+right)>>1;
	fa[mid] = f;
	if(left < mid) child[mid][0] = build(left, mid-1, mid);
	if(right > mid) child[mid][1] = build(mid+1, right, mid);
	updet(mid);
	return mid;
}

int find(int x, int k)      // 查找  
{
	pushdown(x);     // 一定要注意 pushdown 的位置; 
	while(k!=size[child[x][0]]+1)
	{
		
		if(k>size[child[x][0]]+1)
		{
			k -= size[child[x][0]]+1;
			x = child[x][1]; 
		}
		else x = child[x][0]; 
		pushdown(x);
	}
	return x;
}

void print(int x)
{
	if(x==0) return ;
	pushdown(x);
	print(child[x][0]);
	if(x!= 1) printf("%d\n",x - 1);
	print(child[x][1]);
}

int get(int x,int f)
{
	if(child[f][1]==x)
		return 1;
	return 0;
}
// 一定要注意重新与根节点连接的时,从根节点连孩子这条线(这个孩子有两种可能)
//(1) 可能是要移的那个点 (2)也可能是倒数第二步,这步中的x的父亲
// 因为当同向时,先移父亲 后移自己; 
void rotate(int x,int goal)    // 三个位置的断开点,父连子,子连父,需六个重新连接; 
{							
	int y = fa[x];
	int z = fa[y];
	int tt = get(x,y);
	child[y][tt] = child[x][!tt];
	fa[child[y][tt]] = y;
	child[x][!tt] = y;
	if(z!=goal) child[z][get(y,z)] = x;
	// 一定要注意这个连接,让我找了两天啊
	// 也就是splay(x,goal) 把x移动到目标位置的最后一步;不要让 目标位置的父亲连接 x;
	// 因为在spile()分离函数中,已经把移到根节点的节点 的右儿子赋为0了
	//  这个的话 get(y,z) 返回值一定是0;
	// 就这个程序而言,最后一步不要让 目标位置的父亲连接 x;
	// 但是若是 任意往上旋转的话,一定要注意这个连接,最后一步到底该不该要;
	// 也就是一定要记得 除旋转操作函数中,一定要注意其他位置那个位置的连接改变;
	// 这个连接(有父连子,子连父 两种操作);  
	fa[x] = z;
	fa[y] = x;
	updet(y),updet(x);  // 也要注意 updet的位置; 
}

void splay(int x,int t)
{
	while(fa[x]!=t)
	{
		int y =  fa[x], z  = fa[y];
		if(z == t)
			rotate(x,t);
		else if(get(x,y) == get(y,z))     // 同向,先旋转父亲,再旋转儿子 
		  rotate(y,t),rotate(x,t);	
		else                        // 异向,旋转儿子,旋转儿子;
		  rotate(x,t),rotate(x,t);      
	}
}

void spile(int &x, int k, int &left, int &right)
{
	int t1 = find(x, k);
	splay(t1,fa[x]);
	left  = t1;
	right = child[t1][1];
	child[t1][1] = 0;     //这里分离时把刚移到根节点的节点的右儿子赋予0了;
					  	  //在旋转操作中一定要记得;拆断重新连接时,一定要记得; 
	updet(t1);
	x = t1;
}
int merge(int x,int y)
{

	int t1 = find(x,size[x]);
	splay(t1,fa[x]);
	child[t1][1] = y;
	fa[y] = t1;
	updet(t1);
	return t1;
}
// 若直接区间内的数反转的话,就不用,树的分裂 合并了,当时把,当前区间反转后,放到最后面
// 就改变了树的(较)平衡结构,所以要先分裂 分裂 再合并,再合并; 

int main()
{
	scanf("%d%d",&n,&m);
	root = build(1,n+1,0);
	while(m--)
	{
		int x, y, left, mid, right,nextroot; 
		scanf("%d%d", &x, &y);
		spile(root,x,left,nextroot);
		spile(nextroot,y-x+1,mid,right);
		lazy[mid] ^= 1;
		root = merge(merge(left,right),mid);
	}
	print(root);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值