UVA11922 Permutation Transformer(Splay)

有一个(1,2,3...n)的排列,给出m次操作,每次操作翻转区间[l,r],并且把翻转后的区间移动到排列末端,输出最后的排列。

splay的区间翻转操作,第一次写。

开始的时候插入两个虚拟节点0和n+1,这样原本的操作区间[l-1,r+1]就变成了[l,r+2];采用类似线段树的lazy标记,表示该子树表示的区间是否翻转;向下更新的时候下传懒标记,交换子树的编号即可。注意一下输出的时候需要下传懒标记。

#include<cstdio>
#include<iostream>
#define MAXN 100010
using namespace std;
int p,q,n,m,tot,root,a[MAXN],ch[MAXN][2],sz[MAXN],fa[MAXN],rev[MAXN],v[MAXN];
void pushdown(int x)
{
	if(rev[x])
	{
		rev[ch[x][0]] ^= 1;
		rev[ch[x][1]] ^= 1;
		swap(ch[x][0],ch[x][1]);
		rev[x] = 0;
	}
}
void pushup(int x)
{
	sz[x] = sz[ch[x][0]]+sz[ch[x][1]]+1;
}
void rotate(int x)
{
	int y = fa[x],z = fa[y],f = (ch[y][1]==x);
	ch[y][f] = ch[x][!f];
	fa[ch[y][f]] = y;
	ch[x][!f] = y,fa[y] = x;
	fa[x] = z;
	ch[z][ch[z][1]==y] = x;
	pushup(y);
	pushup(x);
}
void splay(int x,int goal)
{
	for(int y; (y=fa[x]) != goal; rotate(x))
	{
		int z = fa[y];
		if(z != goal)
		{
			if((ch[z][0] == y) == (ch[y][0] == x)) rotate(y);
			else rotate(x);
		}
	}
	if(!goal) root = x;
	pushup(x);
}
void rotateto(int k,int goal)
{
	int x = root;
	while(1)
	{
		pushdown(x);
		if(k < sz[ch[x][0]]+1) x = ch[x][0];
		else if(k > sz[ch[x][0]]+1) k -= sz[ch[x][0]]+1,x = ch[x][1];
		else break;
	}
	splay(x,goal);
}
void New(int &x,int val)
{
	x = ++tot;
	v[x] = val;
	sz[x] = 1;
}
void build_tree(int &x,int L,int R,int F)
{
	int mid = (L+R)/2;
	New(x,a[mid]);
	fa[x] = F;
	if(L == R) return;
	if(L < mid) build_tree(ch[x][0],L,mid-1,x);
	if(R > mid) build_tree(ch[x][1],mid+1,R,x);
	pushup(x);
}
void reverse_and_move(int L,int R)
{
	rotateto(L,0);
	rotateto(R+2,root);
	int u = ch[ch[root][1]][0];
	rev[u] ^= 1;
	ch[ch[root][1]][0] = 0;
	pushup(ch[root][1]);
	pushup(root);
	
	rotateto(n+1-sz[u],0);//第一个数其实是节点数!!!
	rotateto(n+2-sz[u],root);
	ch[ch[root][1]][0] = u;
	fa[u] = ch[root][1];
	pushup(ch[root][1]);
	pushup(root);
}
void Print(int x)
{
	if(x == 0) return;
	pushdown(x);
	Print(ch[x][0]);
	if(v[x] != 0)
	printf("%d\n",v[x]);
	Print(ch[x][1]);
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i = 1; i <= n; i++) a[i] = i;
	build_tree(root,0,n+1,0);
	for(int i = 1; i <= m; i++)
	{
		scanf("%d%d",&p,&q);
		reverse_and_move(p,q);
	}
	Print(root);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值