Robotic Sort

一、题目

点此看题

题目描述
通过一系列移动,将某些物品按顺序摆好。规定只能使用如下方式排序: 先找到编号最小的物品的位置P1,将区间[1,P1]反转,再找到编号第二小的物品的位置P2,将区间[2,P2]反转……
输入n和编号,求出所有的Pi,有多组数据, 1 ≤ n ≤ 1 0 5 1\leq n\leq 10^5 1n105
tips
如果有相同的编号,要考虑顺序,可以理解为离散化,越后面的越大

二、解法

区间翻转的经典题,考虑无旋 treap \text{treap} treap
先离散化,然后插入编号进平衡树,这里有一个问题,我们如何找到第几小的,由于这种排序方式满足操作第 i i i次时,全序列的第 i i i小值是 [ i , n ] [i,n] [i,n]中的最小值,我们就可以让平衡树维护一个编号的最小值,每次操作时先分裂出来 [ i , n ] [i,n] [i,n],找到它最小的编号的排名 t t t,就可以把 [ i , t − i + 1 ] [i,t-i+1] [i,ti+1]分裂出来,打上标记,然后拼回去,找排名是沿着 min ⁡ = i \min=i min=i子树找,然后就能找到排名了。
无旋 treap \text{treap} treap还是很好,容易写,功能也比较齐全。

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <ctime>
using namespace std;
const int MAXN = 100005; 
int read()
{
	int x=0,flag=1;char c;
	while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
	while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return x*flag;
}
int n,rt,num[MAXN],pos[MAXN];
int ch[MAXN][2],Min[MAXN],siz[MAXN],hp[MAXN],fl[MAXN];
bool cmp(int x,int y)
{
	if(num[x]==num[y])
		return x<y;
	return num[x]<num[y];
}
struct node
{
	int p[2];
	node() {p[0]=p[1]=0;}
}emp;
void up(int x)
{
	siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;
	Min[x]=min(num[x],min(Min[ch[x][0]],Min[ch[x][1]]));
}
void down(int x)
{
	fl[ch[x][0]]^=1;fl[ch[x][1]]^=1;
	swap(ch[x][0],ch[x][1]);
	fl[x]=0;
}
node split(int x,int s)
{
	if(!x) return emp;
	if(fl[x]) down(x);
	node y;
	if(siz[ch[x][0]]>=s)
	{
		y=split(ch[x][0],s);
		ch[x][0]=y.p[1];
		y.p[1]=x;
	}
	else
	{
		y=split(ch[x][1],s-siz[ch[x][0]]-1);
		ch[x][1]=y.p[0];
		y.p[0]=x;
	}
	up(x);
	return y;
}
int merge(int x,int y)
{
	if(!x || !y) return x+y;
	if(hp[x]<hp[y])
	{
		if(fl[x]) down(x);
		ch[x][1]=merge(ch[x][1],y);
		up(x);
		return x;
	}
	if(fl[y]) down(y);
	ch[y][0]=merge(x,ch[y][0]);
	up(y);
	return y;
}
int find(int x,int v)
{
	if(!x) return 0;
	if(fl[x]) down(x);
	if(num[x]==v) return siz[ch[x][0]]+1;
	if(ch[x][0] && Min[ch[x][0]]==v)
		return find(ch[x][0],v);
	if(ch[x][1]) 
		return siz[ch[x][0]]+1+find(ch[x][1],v);
	return 0;
}
int main()
{
	srand(time(0));
	while(~scanf("%d",&n) && n)
	{
		rt=0;Min[0]=0x3f3f3f3f;
		for(int i=1;i<=n;i++)
			num[i]=read(),pos[i]=i;
		sort(pos+1,pos+1+n,cmp);
		for(int i=1;i<=n;i++)
			num[pos[i]]=i;
		for(int i=1;i<=n;i++)
		{
			ch[i][0]=ch[i][1]=0;
			Min[i]=num[i];siz[i]=1;fl[i]=0;hp[i]=rand();
			rt=merge(rt,i);
		}
		for(int i=1;i<n;i++)
		{
			node x=split(rt,i-1);
			int t=find(x.p[1],i);
			node y=split(x.p[1],t);
			fl[y.p[0]]^=1;
			rt=merge(x.p[0],merge(y.p[0],y.p[1]));
			printf("%d ",t+i-1);
		}
		printf("%d\n",n);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值