题意:
给你一个长度为
n
n
n的序列,现在有一种对序列排序的方法:假设当前最小的前
i
i
i个已经找到了,我们找到
i
+
1
i+1
i+1到
n
n
n里面最早出现的最小值,设其出现位置为
j
j
j,我们将区间
[
i
+
1
,
j
]
[i+1,j]
[i+1,j]翻转来排序。输出每次翻转前要找的最小值的位置。
n
<
=
1
e
5
n<=1e5
n<=1e5
题解:
首先先吐槽一下,最近可能我实在是太蠢了,这个题硬是写了一天半。网上题解看了不少,都没看懂,感觉好像他们建的平衡树的下标含义和我似乎都不一样。于是最后还是自己yy了一种做法。
这种区间翻转的题,我们还是类似文艺平衡树的方法去用splay维护,为了提取区间方便,我们还是在头尾各加一个节点。我这里splay维护的顺序就是序列的位置顺序,也就是最初中序遍历splay就能得到原序列,只不过我没有在splay的节点上记录数值。
我将每个位置的数值和下标排序,得到这个位置应该是第几次被翻转,记录到了 r k rk rk数组中。对于第 i i i次,我们找到翻转要提取区间,提取区间的前驱是 r k [ i − 1 ] rk[i-1] rk[i−1],后继是 r k [ i ] rk[i] rk[i]的后继,将 r k [ i − 1 ] rk[i-1] rk[i−1]转到根,再将那个后继转到根的右儿子,这样区间就被提取到后继的左子树了,就是经典的splay提取区间操作。然后对提取的区间打上翻转标记。每次输出的答案就是将 r k [ i ] rk[i] rk[i]转到根后左子树的大小,本来应该要加一,但是我们左端加了一个点,所以就不用加一了。splay维护的唯一量就是所在子树的size。
特别说一下区间翻转标记的pushdown问题。这里感觉对pushdown有了一些新的理解,但是不保证的对的。感觉网上始终没人说splay的区间标记什么时候应该pushdown,于是我们能见到各自版本的,有的只有在逐层向下查找时pushdown,除了逐层向下时一定要pushdown之外,有的只在splay里pushdown,有的只在rotate里面pushdown,有的既在splay里pushdown又在rotate里pushdown。我这里的写法是在逐层向下查找时pushdown和在splay里pushdown,顺序都是先根节点后子节点。我的理解是,只要需要用到左右儿子信息的时候,就要先pushdown,只要能满足这个,就可以了。而可以不在rotate里面写是因为splay里面已经先对要rotate操作的节点进行了一些条件判断,而在这些判断之前做完pushdown,就可以不在rotate里面pushdown了。
另外这是我第一次把splay写到一个结构体了,虽然这里不写到结构体里应该也没什么区别。
不知道是不是和网上很多题解不太一样的做法,希望能给大家提供一种新思路。
代码:
#include <bits/stdc++.h>
using namespace std;
int n,a[100010],root,rk[100010];
struct node
{
int sz,c[2],rev,f;
}tr[400010];
struct qwq
{
int id,v;
}b[101000];
int cmp(qwq x,qwq y)
{
if(x.v==y.v)
return x.id<y.id;
return x.v<y.v;
}
inline void pushup(int rt)
{
tr[rt].sz=tr[tr[rt].c[0]].sz+tr[tr[rt].c[1]].sz+1;
}
inline void build(int rt,int l,int r)
{
int mid=(l+r)>>1;
if(rt)
{
if(mid<rt)
tr[rt].c[0]=mid;
else
tr[rt].c[1]=mid;
}
tr[mid].f=rt;
tr[mid].sz=1;
if(l!=r)
{
if(l<mid)
build(mid,l,mid-1);
if(mid+1<=r)
build(mid,mid+1,r);
}
pushup(mid);
}
inline void pushdown(int rt)
{
if(tr[rt].rev)
{
if(tr[rt].c[0])
tr[tr[rt].c[0]].rev^=1;
if(tr[rt].c[1])
tr[tr[rt].c[1]].rev^=1;
swap(tr[rt].c[0],tr[rt].c[1]);
tr[rt].rev=0;
}
}
inline void rotate(int x)
{
int y=tr[x].f,z=tr[y].f,k=tr[y].c[1]==x,w=tr[x].c[!k];
if(y!=root)
tr[z].c[tr[z].c[1]==y]=x;
tr[x].c[!k]=y;
tr[y].c[k]=w;
if(w)
tr[w].f=y;
tr[y].f=x;
tr[x].f=z;
pushup(y);
pushup(x);
}
inline void splay(int x,int rt)
{
while(tr[x].f!=rt)
{
int y=tr[x].f,z=tr[y].f;
pushdown(z);
pushdown(y);
pushdown(x);
if(tr[y].f!=rt)
{
if(tr[z].c[0]==y ^ tr[y].c[0]==x)
rotate(x);
else
rotate(y);
}
rotate(x);
if(rt==0)
root=x;
}
}
inline int find()
{
int x=root;
pushdown(root);
x=tr[root].c[1];
while(x)
{
pushdown(x);
if(tr[x].c[0])
x=tr[x].c[0];
else
return x;
}
}
int main()
{
scanf("%d",&n);
for(int i=2;i<=n+1;++i)
scanf("%d",&a[i]);
for(int i=2;i<=n+1;++i)
{
b[i-1].id=i;
b[i-1].v=a[i];
}
sort(b+1,b+n+1,cmp);
for(int i=1;i<=n;++i)
rk[i+1]=b[i].id;
rk[1]=1;
rk[n+2]=n+2;
a[1]=1e9;
a[n+2]=1e9;
a[0]=1e9;
build(0,1,n+2);
root=(1+n+2)>>1;
for(int i=2;i<=n+1;++i)
{
splay(rk[i],0);
int x=tr[tr[rk[i]].c[0]].sz;
int yy=find();
printf("%d ",x);
splay(rk[i-1],0);
splay(yy,rk[i-1]);
int y=tr[root].c[1],z=tr[y].c[0];
tr[z].rev^=1;
}
return 0;
}