codevs1743 反转卡片

本文介绍了一种使用Splay树解决特定卡片反转问题的方法。通过构建Splay树并运用旋转操作,实现了对一系列卡片的高效反转,直到首张卡片上的数字变为1。文章详细解释了Splay树的构建过程及其在解决问题中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

                         1743 反转卡片
时间限制: 2 s
空间限制: 256000 KB
题目等级 : 大师 Master
题解
题目描述 Description
【dzy493941464|yywyzdzr原创】

小A将N张卡片整齐地排成一排,其中每张卡片上写了1~N的一个整数,每张卡片上的数各不相同。

比如下图是N=5的一种情况:3 4 2 1 5

接下来你需要按小A的要求反转卡片,使得左数第一张卡片上的数字是1。操作方法:令左数第一张卡片上的数是K,如果K=1则停止操作,否则将左数第1~K张卡片反转。

第一次(K=3)反转后得到:2 4 3 1 5

第二次(K=2)反转后得到:4 2 3 1 5

第三次(K=4)反转后得到:1 3 2 4 5

可见反转3次后,左数第一张卡片上的数变成了1,操作停止。

你的任务是,对于一种排列情况,计算要反转的次数。你可以假设小A不会让你操作超过100000次。

输入描述 Input Description
第1行一个整数N;

第2行N个整数,为1~N的一个全排列。

输出描述 Output Description
仅1行,输出一个整数表示要操作的次数。

如果经过有限次操作仍无法满足要求,输出-1。

样例输入 Sample Input
5

3 4 2 1 5





样例输出 Sample Output
3

数据范围及提示 Data Size & Hint
0<N≤300,000。


一道splay旋转区间

之前写了bzoj3223: Tyvj 1729 文艺平衡树

但是理解还不够深刻

还没能透彻理解区间旋转

因此刚开始找左数第一个数时写错了

直接写成了tr[2].v(1是虚拟结点,翻转用的)

而splay旋转时是直接修改树的左右孩子(感觉是类似于指针一样的东西,就是指向左右孩子的树根)

所以tr[2].v可能会转到其他地方

所以要先用find函数找到排名为2的结点的位置

嗯,理解深刻多了

(如有错误请大神指教)

Ps:Sheldon帅炸啦!!!!!!

#include<cstdio>
#include<iostream>
const int N=300050;
struct node
{
	int fa,c[2],size,v,lazy;
}tr[N];
int n,root;
void pushup(int k)
{
	tr[k].size=tr[tr[k].c[0]].size+tr[tr[k].c[1]].size+1;
}
void pushdown(int k)
{
	if(tr[k].lazy)
	{
		std::swap(tr[k].c[0],tr[k].c[1]);
		tr[tr[k].c[0]].lazy^=1;
		tr[tr[k].c[1]].lazy^=1;
		tr[k].lazy=0;
	}
}
void build(int l,int r,int fa)
{
	if(l>r)	return ;
	if(l==r)
	{
		tr[l].fa=fa;tr[l].size=1;
		if(l<fa)	tr[fa].c[0]=l;
		else 		tr[fa].c[1]=l;
	}
	int mid=(l+r)>>1;
	build(l,mid-1,mid);build(mid+1,r,mid);
	tr[mid].fa=fa;pushup(mid);
	if(mid<fa)	tr[fa].c[0]=mid;
	else 		tr[fa].c[1]=mid;
}
void rotate(int x,int &k)
{
	int y=tr[x].fa,z=tr[y].fa,    l,r;
	if(tr[y].c[0]==x)l=0;
	else l=1;
	r=l^1;
	if(y==k)k=x;	
	else 
	{
		if(tr[z].c[0]==y)tr[z].c[0]=x;
		else tr[z].c[1]=x;
	}
	tr[x].fa=z;	tr[y].fa=x;
	tr[tr[x].c[r]].fa=y;
	tr[y].c[l]=tr[x].c[r];
	tr[x].c[r]=y;
	pushup(y);	pushup(x);
}
void splay(int x,int &k)
{
	while(x!=k)
	{
		int y=tr[x].fa,z=tr[y].fa;
		if(y!=k)
		{
			if((tr[y].c[0]==x)^(tr[z].c[0]==y))	rotate(x,k);
			else rotate(y,k);
		}
		rotate(x,k);
	}
}
int find(int k,int rank)
{
	pushdown(k);
	if(tr[tr[k].c[0]].size+1==rank)	return k;
	if(tr[tr[k].c[0]].size+1>rank)	return find(tr[k].c[0],rank);
	else return find(tr[k].c[1],rank-tr[tr[k].c[0]].size-1);
}
void dfs(int ro)
{   
    pushdown(ro);
    if(tr[ro].c[0])dfs(tr[ro].c[0]);
    if(ro!=1&&ro!=n+2)printf("%d ",tr[ro].v);
    if(tr[ro].c[1])dfs(tr[ro].c[1]);
}
void rever(int l,int r)
{
	int x=find(root,l);
	splay(x,root);
	int y=find(root,r+2);
	splay(y,tr[root].c[1]);
	tr[tr[y].c[0]].lazy^=1;
}
int main()
{
	scanf("%d",&n); 
	for(int i=1;i<=n;i++)
	scanf("%d",&tr[i+1].v);
	build(1,n+2,n+5);//该序列为   2  ~  n+1  
	//额外节点为  1  和  n+2 
	root=tr[n+5].c[0];
	for(int ans=0;ans<=100000;ans++)
	{
		int k=tr[find(root,2)].v;
		if(k!=1)	rever(1,k);
		else 
		{
			printf("%d\n",ans);
			return 0;
		}
	}
	printf("-1\n");
	return 0;
}


转载于:https://www.cnblogs.com/Brian551/p/7353036.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值