普通的平衡树操作还是Treap更加简单点,但是Splay强大的一个地方就是对区间的操作,可以做一下线段树搞不定的东西~~
题目:http://acm.hdu.edu.cn/showproblem.php?pid=1890
题意:
区间翻转,使得所给序列有序,问所需要的翻转长度的和?
分析:
我们每次可以先将“最小”的点旋转到根,那么根左边的区间自然就是要翻转的区间了,于是我们对根左边的区间打上翻转的标记,然后将根删除,如此反复N-1遍。对于每次要输出的值,假设我们现在试图令第i个block到位,那么把这个点旋转到根后,输出i+size[left[T]]即可,其中T表示根。当然,最后还要输出一个N。
然而,我们要如何知道每次该旋转哪个block呢?即便我们知道该旋转哪个block,那这个block在splay中又处于哪个位置呢?对于第一个问题,我们可以先将block排个序,这样自然就知道每次该旋转哪个block了。对于第二个问题,我们可以在建树的过程中把每个block所在的节点的标号存下来即可。这样我们每次就可以很方便的知道该旋转哪个block了,同时也能方便地知道它在splay中的位置,但是这时我们不能直接调用splay操作把这个点旋转上去,因为这个节点的上方有可能还有一些翻转的标记没有下传,因此我们要先从这个节点开始向上找到根,并在找的过程中记录一下路径,然后再从根节点向下沿记录好的路径去找要旋转的节点,并在找的过程中下传翻转标记。
copy自:http://www.cnblogs.com/staginner/archive/2012/04/01/2427883.html
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define L ch[x][0]
#define R ch[x][1]
#define KT (ch[ ch[rt][1] ][0])
const int maxn = 100010;
struct SplayTree {
int sz[maxn];
int ch[maxn][2];
int pre[maxn];
int rt,top;
inline void down(int x) {
if(flip[x]) {
flip[L]^=1;
flip[R]^=1;
swap(L,R);
flip[x]=0;
}
}
inline void up(int x) {
sz[x]=sz[L]+sz[R]+1;
}
inline void Rotate(int x,int f) {
int y=pre[x];
down(y);
down(x);
ch[y][!f]=ch[x][f];
pre[ch[x][f]]=y;
pre[x]=pre[y];
if(pre[x])ch[pre[y]][ ch[pre[y]][1]==y]=x;
ch[x][f]=y;
pre[y]=x;
up(y);
}
inline void Splay(int x,int goal) {
down(x);
while(pre[x]!=goal) {
down(pre[pre[x]]);
down(pre[x]);
down(x);
if(pre[pre[x]]==goal)Rotate(x,ch[pre[x]][0]==x);
else {
int y=pre[x],z=pre[y];
int f=(ch[z][0]==y);
if(ch[y][f]==x)Rotate(x,!f),Rotate(x,f);
else Rotate(y,f),Rotate(x,f);
}
}
up(x);
if(goal==0)rt=x;
}
inline void RTO(int k,int goal) {
int x=rt;
down(x);
while(sz[L]+1!=k) {
if(k<sz[L]+1)x=L;
else k-=sz[L]+1,x=R;
down(x);
}
Splay(x,goal);
}
inline void newNode(int &x,int c,int f) {
x=++top;
flip[x]=0;
L=R=0;
pre[x]=f;
sz[x]=1;
val[x]=c;
}
inline void build(int &x,int l,int r,int f) {
if(l>r)return;
int m=l+r>>1;
newNode(x,id[m],f);
mp[id[m]]=x;
build(L,l,m-1,x);
build(R,m+1,r,x);
pre[x]=f;
up(x);
}
inline void init(int n) {
ch[0][0]=ch[0][1]=pre[0]=sz[0]=0;
rt=top=0;
flip[0]=val[0]=0;
for(int i=1; i<=n; i++)scanf("%d",&num[i].first),num[i].second=i;
sort(num+1,num+1+n);
for(int i=1; i<=n; i++)id[num[i].second]=i; //离散化
build(rt,1,n,0);
}
inline void Del() {
int t=rt;
if(ch[rt][1]) {
rt=ch[rt][1];
RTO(1,0);
ch[rt][0]=ch[t][0];
if(ch[rt][0])pre[ch[rt][0]]=rt;
} else rt=ch[rt][0];
pre[rt]=0;
up(rt);
}
inline void solve(int n) {
for(int i=1; i<=n; i++) {
Splay(mp[i],0);
printf("%d",i+sz[ch[rt][0]]);
printf(i==n?"\n":" ");
flip[ch[rt][0]]^=1;
Del();
}
}
pair<int,int>num[maxn];
int id[maxn];//离散化后的序列
int mp[maxn]; //每个数对应的结点
int flip[maxn];
int val[maxn];
} spt;
int main() {
int n;
//freopen("f.txt","r",stdin);
while(~scanf("%d",&n)&&n) {
spt.init(n);
spt.solve(n);
}
return 0;
}