HDU 1890(splay tree 区间翻转)
前后折腾了一天半时间才搞定。。从学习lazy到理解代码。。—_—||
题意是说每次把第i大的数所在位置和第i个位置之间翻转,输出每个数在翻转前的位置。
首先我们要想到,在splay tree 中,对于根节点来说,左子树的大小+1就是它在数组中的位置(从1开始标号),左子树的各元素也是在数列中位于根结点左边的
我们现在用splay tree来维护未排序的序列,那对于题目要求的翻转操作来说,对象就仅仅是根节点的左子树了
我们要做的事情可以抽象成这样:每次把第i大的结点旋转至根,再把根节点的左子树打上rev标记,输出答案i+s[ch[root][0]],然后删除根节点
对于答案的理解,可以分为两部分,首先i是至在数列左边已排序的序列(包括待排序的根节点),s[ch[root][0]]则是在目前操作的结点左边的节点数
对于lazy标记的理解建议最好画画图
现在要解决的问题有两个:1、如何找到第i大的结点 2.确定该结点在splay tree中的位置
对于第一个问题,我们把数列先排序(记得把序列的id同时捆绑),那我们只需要依次对这n个数依次处理就好
第二个问题要稍微难理解一些。这里也稍稍用了离散化的思想。我们把数列按照要求排序后,按照id(原序列次序)相当于离散成1~n这n个数。
我们建立一棵结点编号为1~n的平衡树作为初始状态
则不难理解这棵树维护的是排序前每个数的id,也就是说维护的是原序列
其次要先理解splay tree中结点编号和该结点在原数列的下标的关系——相等。
举个例子:现在要操作第3个结点,他的id是5,也就是说这是第3大的结点,在原序列中在第5个位置。
那我把5号结点旋转到根,看看左子树的大小(相当于看看原序列中5号位置左边还剩几个元素,因为第1第2大的都删了)
代码是参考网上的。
#include"cstdio" #include"queue" #include"cmath" #include"stack" #include"iostream" #include"algorithm" #include"cstring" #include"queue" #include"map" #include"vector" #define ll long long #define mems(a,b) memset(a,b,sizeof(a)) using namespace std; const int MAXN = 1e5+50; const int MAXE = 200500; const int INF = 0x3f3f3f; int pre[MAXN],ch[MAXN][2],rev[MAXN],s[MAXN]; int root,n; struct Node{ int w,id; }node[MAXN]; bool cmp(Node a,Node b){ if(a.w==b.w) return a.id<b.id; return a.w<b.w; } void newnode(int &pos,int fa,int k){ pos=k; pre[pos]=fa; ch[pos][0]=ch[pos][1]=0; rev[pos]=0; s[pos]=1; } void pushdown(int x){ if(rev[x]){ rev[ch[x][0]]^=1; rev[ch[x][1]]^=1; swap(ch[x][0],ch[x][1]); rev[x]=0; } } void pushup(int x){ s[x]=s[ch[x][0]]+s[ch[x][1]]+1; } void build(int &x,int l,int r,int fa){///建立平衡树 if(l>r) return; int mid=(l+r)>>1; newnode(x,fa,mid); build(ch[x][0],l,mid-1,x); build(ch[x][1],mid+1,r,x); pushup(x); } void init(){ root=0; sort(node+1,node+1+n,cmp); build(root,1,n,0); } void Rotate(int x,int kind){ int fa=pre[x]; pushdown(fa); pushdown(x); ///注意先后次序 ch[fa][!kind]=ch[x][kind]; pre[ch[x][kind]]=fa; if(pre[fa]) ch[pre[fa]][ch[pre[fa]][1]==fa]=x; pre[x]=pre[fa]; ch[x][kind]=fa; pre[fa]=x; pushup(fa); ///注意先后次序 pushup(x); } void Splay(int r,int goal){ pushdown(r); while(pre[r]!=goal){ if(pre[pre[r]]==goal){ pushdown(pre[r]); ///不可省去,会影响ch[pre[r]][0],下面类似语句同理不可省 pushdown(r); Rotate(r,ch[pre[r]][0]==r); } else{ int fa=pre[r]; pushdown(pre[fa]); ///先pushdown再求kind -_-|| 因为这个TLE了半天 pushdown(pre[r]); pushdown(r); int kind=(ch[pre[fa]][0]==fa); if(ch[fa][kind]==r){ Rotate(r,!kind); Rotate(r,kind); } else{ Rotate(fa,kind); Rotate(r,kind); } } } pushup(r); if(!goal) root=r; } int getmax(int x){ ///寻找比x小但是最接近x的数 pushdown(x); while(ch[x][1]){ x=ch[x][1]; pushdown(x); } return x; } void del(int x){ if(!ch[root][0]){ root=ch[root][1]; pre[root]=0; } else{ int t=getmax(ch[root][0]); Splay(t,root); ch[t][1]=ch[root][1]; pre[ch[root][1]]=t; root=t; pre[root]=0; pushup(root); } } int main(){ //freopen("in.txt","r",stdin); while(scanf("%d",&n)&&n){ for(int i=1;i<=n;i++){ scanf("%d",&node[i].w); node[i].id=i; } init(); for(int i=1;i<n;i++){ Splay(node[i].id,0); rev[ch[root][0]]^=1; printf("%d ",i+s[ch[root][0]]); del(root); } printf("%d\n",n); } return 0; }