Splay区间翻转操作
[Cerc2007]robotic sort
在一个车间里有N(1<=N<=100000)个零件排成一列,已知他们各自的高度,现在要将他们按高度排列成升序序列
规定只能使用如下方法:
找到最低的零件的位置P1,将区间[1,P1]反转,再找到第二低的零件的位置P2,将区间[2,P2]反转……
要求你的程序输出P1,P2,P3…
如果有一样高的零件,那么优先处理在原始序列中靠前的零件。
对于样例
3 4 5 1 6 2
第一次,先找到“1”的位置,它在第四个位置,它将与第一个位置这一段的数字反转
得到1 5 4 3 6 2,对应的输出为4
第二次,先找到"2"的位置,它在第六个位置,它将与第二个位置这一段的数字反转
得到1 2 6 3 4 5,对应的输出为6
第三次,先找到"3"的位置,它在第四个位置,它将与第三个位置这一段的数字反转
得到1 2 3 6 4 5,对应的输出为4
样例输入
6
3 4 5 1 6 2
样例输出
4 6 4 5 6 6
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; int n,a[100010],key[100010],b[100010],p[100010],root,sz; int fa[100010],ch[100010][3],size[100010],rev[100010],pos[100010]; int cmp(int x,int y) { return a[x]<a[y]||a[x]==a[y]&&x<y; } int get(int x) { return ch[fa[x]][1]==x; } void update(int x) { int l=ch[x][0],r=ch[x][1]; size[x]=1; if(l) size[x]+=size[l]; if(r) size[x]+=size[r]; } void pushdown(int x) { int l=ch[x][0],r=ch[x][1]; if(!rev[x]) return ; rev[l]^=1; rev[r]^=1; swap(ch[x][0],ch[x][1]); rev[x]=0; } void rotate(int x) { pushdown(fa[x]); pushdown(x); int f=fa[x]; int ff=fa[f]; int sum=get(x); //建立x与其祖父点ff之间的关系 if(ff) ch[ff][ch[ff][1]==f]=x; fa[x]=ff; //重新建立x与父亲点f的关系 //f变成x的子结点 fa[f]=x; //将x的左(右)结点变成f的右(左)结点 ch[f][sum]=ch[x][sum^1]; fa[ch[f][sum]]=f; ch[x][sum^1]=f; //想清楚上面语句的逻辑关系 update(f); update(x); } void splay(int x,int tar) { for(int f;(f=fa[x])!=tar;rotate(x)) if(fa[f]!=tar) rotate(get(x)==get(f)?f:x); if(!tar) root=x; } int find(int x) { int rt=root; while(true) { pushdown(rt); if(x<=size[ch[rt][0]]) rt=ch[rt][0]; else { int num=size[ch[rt][0]]+1; if(num==x) return rt; x-=num; rt=ch[rt][1]; } } } int build(int l,int r) { if(l==r) { sz++; pos[b[l]]=sz; size[sz]=1; return sz; } if(l>r) return 0; int now=++sz; int mid=(l+r)/2; pos[b[mid]]=now; ch[now][0]=build(l,mid-1); ch[now][1]=build(mid+1,r); fa[ch[now][0]]=now; fa[ch[now][1]]=now; size[now]=size[ch[now][0]]+size[ch[now][1]]+1; return now; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); p[i]=i; } sort(p+1,p+n+1,cmp); int cnt=0; b[1]=0; //新加一个头结点,值最小 b[n+2]=n+2; //新加一个尾结点,值最大 for(int i=1;i<=n;i++) b[p[i]+1]=i; root=build(1,n+2); for(int i=1;i<=n;i++) { splay(pos[i],0); int ans=size[ch[root][0]]; printf("%d",ans); if(i!=n) printf(" "); else printf("\n"); splay(find(i),0); splay(find(ans+2),find(i)); rev[ch[ch[root][1]][0]]^=1; } return 0; }