题意
(n)个物品,依次排列,每个物品都有一个高度(hi)
(n)次操作,第(i)次操作将区间 [位置(i),第(i)低的物品(多个时取靠左的优先)的位置]翻转
回答一个序列,第(i)个数表示每次操作前第(i)低的物品所在位置
思路
利用(splay)进行多次区间翻转的一道题。
建立(n)个节点,节点(i)表示位置(i),一开始我们将splay类似线段树一样建好,其结点中序遍历就是(1,2,...,n−1,n)
我们将原本的物品高度升序排列,高度相同按位置升序排列。
然后每次就是找到第(i)高度的物品的结点(k),此时(splay)的结点(k)在(splay)中序遍历所在的位置(从左到右数第几个),就是经过(i−1)次翻这个位置所对应的是最初始序列的第(k)个位置。
将结点(k)移动到根,然后查询左儿子子树大小就是答案了。
翻转原序列就是(splay)的一个基本操作了。
注意:每次要先把元素旋转到根得到他的排名在反转区间,所以在这里(splay)里面也要加(push)下推。
#include<bits/stdc++.h>
using namespace std;
const int maxx = 1e5+10;
const int inf = 0x3f3f3f3f;
struct node
{
int p,id;
bool operator < (const node &t)const
{
if(p==t.p)return id<t.id;
return p<t.p;
}
}a[maxx];
int ch[maxx][2],fa[maxx],siz[maxx],lazy[maxx];
int rt;
int get(int x)
{
return ch[fa[x]][1]==x;
}
void update(int x)
{
if(x)siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;
}
void pushdown(int x)
{
if(x&&lazy[x])
{
lazy[ch[x][0]]^=1;
lazy[ch[x][1]]^=1;
swap(ch[x][0],ch[x][1]);
lazy[x]=0;
}
}
void rotate(int x)
{
int y=fa[x],z=fa[y],k=get(x);
pushdown(x);pushdown(y);
ch[y][k]=ch[x][k^1];fa[ch[y][k]]=y;
ch[x][k^1]=y;fa[y]=x;fa[x]=z;
if(z)ch[z][ch[z][1]==y]=x;
update(y);update(x);
}
void splay(int x,int goal)
{
for(int y;(y=fa[x])!=goal;rotate(x))
{
pushdown(fa[y]),pushdown(y),pushdown(x);
if(fa[y]!=goal)rotate((get(x)==get(y))?y:x);
}
if(goal==0)rt=x;
}
int findkth(int k)
{
int x=rt;
while(1)
{
pushdown(x);
if(k<=siz[ch[x][0]])x=ch[x][0];
else
{
k-=siz[ch[x][0]]+1;
if(!k)return x;
x=ch[x][1];
}
}
}
int build(int l,int r,int f)
{
if(l>r)return 0;
int mid=(l+r)/2;
fa[mid]=f;
ch[mid][0]=build(l,mid-1,mid);
ch[mid][1]=build(mid+1,r,mid);
update(mid);
return mid;
}
void reverse(int x,int y)
{
int l=x-1,r=y+1;
l=findkth(l);r=findkth(r);
splay(l,0);splay(r,l);
lazy[ch[ch[rt][1]][0]]^=1;
}
int main()
{
int n;
scanf("%d",&n);
for(int i=2;i<=n+1;i++)
scanf("%d",&a[i].p),a[i].id=i;
a[1].id=1,a[1].p=-inf;
a[n+2].id=n+2,a[n+2].p=inf;
sort(a+1,a+1+n+2);
rt=build(1,n+2,0);
for(int i=2;i<=n+1;i++)
{
splay(a[i].id,0);
int ans=siz[ch[rt][0]]+1;
printf("%d ",ans-1);
reverse(i,ans);
}
}