hdu 1890 Robotic Sort
题意:给出一个n个数的数组,给他进行排序。排序的过程是这样的,对于第i次找到第i大的数在哪一个位置,输出这个位置,然后从第i个位置开始到找到的位置,将这个区间翻转一遍(此时第i大的数肯定在i位置了,显而易见),进行n-1次后这个数列就有序了。
解题思路:splay tree 伸展树,要记录的延迟标记是一个翻转标记,col[i]表示翻转了几次(奇数次相当于要翻转,偶数次其实就相当于不用翻转,翻转就是左右儿子互换一下嘛)。。先将这n个数离散化一下,离散化成1-n的数,然后将第i大的数(也就是i)在splay tree里的节点标号记录一下,为id[i](这在build的时候很方便的就可以记录了)。对于第i次操作,将id[i] splay到根,但在这之前还有一步操作,我们要顺着id[i]往上,一直到根,将这一路上的翻转标记都传递下来,并且把要翻转的都进行翻转,否则会出错的(我就这里没写,debug了一下午,后来模拟了一组随机数据,才找到错误,画了两张草稿纸的树。。)。这样之后就可以放心的把id[i] splay到根了,那么对于i的位置就很好找了,就是i+size[son[0][id[i]]],因为之前的数,我都将它们所在的节点从树上删除掉了,所有要用左儿子的size+i。那么我们怎样进行这个删除操作呢?当前的id[i],这个节点是在根上的,那么我们就是要把这个根去掉,然后找一个新的根了。这个还是比较好想的。找到左儿子最大的那个节点(就是中序遍历最后的那个节点,splay tree本身就是一颗中序遍历的树),这个其实就一直找右儿子就可以了,而且找之前要先push_down,否则右儿子的信息是不真实的。找到这个节点后,把它splay 到要删除的根下面,我们就是要把这个节点当做根,接下来我所说的根就是这个节点了。为什么这个节点可以当做根了呢?此时,这个根是没有右儿子的,而对于原来的根,它的右子树的所有节点的在原树的中序遍历的时间戳一定大于这个节点,也就是说我们可以把这个右子树直接挂在新的根下面了,那么新的根就生成了,而原来的根就相当于被我剔除了。这个过程中,将节点的信息仔细维护好就行了。
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std ; const int maxn = 100011 ; int col[maxn] , id[maxn] , num[maxn] ; int size[maxn] , son[2][maxn] , fa[maxn] , tot , t[maxn] ; void push_down ( int rt ) { if ( col[rt] ) { int ls = son[0][rt] , rs = son[1][rt] ; if ( ls ) col[ls] += col[rt] ; if ( rs ) col[rs] += col[rt] ; if ( col[rt] % 2 ) son[0][rt] = rs , son[1][rt] = ls ; col[rt] = 0 ; } } void push_up ( int rt ) { size[rt] = 1 ; if ( son[0][rt] ) size[rt] += size[son[0][rt]] ; if ( son[1][rt] ) size[rt] += size[son[1][rt]] ; } int new_node ( int v ) { size[++tot] = 1 ; son[0][tot] = son[1][tot] = fa[tot] = 0 ; col[tot] = 0 ; id[v] = tot ; return tot ; } int build ( int l , int r ) { if ( l > r ) return 0 ; int mid = l + r >> 1 ; int temp = new_node ( num[mid] ) ; son[0][temp] = build ( l , mid - 1 ) ; if ( son[0][temp] ) fa[son[0][temp]] = temp , size[temp] += size[son[0][temp]] ; son[1][temp] = build ( mid + 1 , r ) ; if ( son[1][temp] ) fa[son[1][temp]] = temp , size[temp] += size[son[1][temp]] ; return temp ; } void rot ( int rt , int c ) { int y = fa[rt] , z = fa[y] ; push_down ( y ) , push_down ( rt ) ; son[!c][y] = son[c][rt] ; if ( son[c][rt] ) fa[son[c][rt]] = y ; fa[rt] = z ; if ( z ) { if ( y == son[0][z] ) son[0][z] = rt ; else son[1][z] = rt ; } son[c][rt] = y , fa[y] = rt ; push_up ( y ) ; } void splay ( int x , int to ) { push_down ( x ) ; while ( fa[x] != to ) { if ( fa[fa[x]] == to ) rot ( x , x == son[0][fa[x]] ) ; else { int y = fa[x] , z = fa[y] ; if ( x == son[0][y] ) { if ( y == son[0][z] ) rot ( y , 1 ) , rot ( x , 1 ) ; else rot ( x , 1 ) , rot ( x , 0 ) ; } else { if ( y == son[1][z] ) rot ( y , 0 ) , rot ( x , 0 ) ; else rot ( x , 0 ) , rot ( x , 1 ) ; } } } push_up ( x ) ; } int jion ( int r1 , int r2 , int root ) { int x = r1 ; push_down ( x ) ; while ( son[1][x] ) { x = son[1][x] ; push_down ( x ) ; } splay ( x , root ) ; son[1][x] = son[1][root] ; if ( son[1][root] ) fa[son[1][root]] = x ; return x ; } int cmp ( int i , int j ) { if ( num[i] == num[j] ) return i < j ; return num[i] < num[j] ; } void search ( int x , int to ) { if ( x == to ) { push_down ( x ) ; return ; } search ( fa[x] , to ) ; push_down ( x ) ; } int main () { int n , i , j , k ; while ( scanf ( "%d" , &n ) != EOF ) { if ( n == 0 ) break ; for ( i = 1 ; i <= n ; i ++ ) scanf ( "%d" , &num[i] ) , t[i] = i ; sort ( t + 1 , t + n + 1 , cmp ) ; for ( i = 1 ; i <= n ; i ++ ) num[t[i]] = i ; tot = 0 ; int rt = build ( 1 , n ) ; for ( i = 1 ; i <= n ; i ++ ) { search ( id[i] , rt ) ; splay ( id[i] , 0 ) ; rt = id[i] ; int ans = i ; if ( son[0][rt] ) ans += size[son[0][rt]] , col[son[0][rt]] ++ ; printf ( "%d" , ans ) ; if ( i != n ) { printf ( " " ) ; if ( !son[0][rt] ) rt = son[1][rt] ; else rt = jion ( son[0][rt] , son[1][rt] , rt ) ; fa[rt] = 0 ; } } puts ( "" ) ; } } /* 10 7 8 4 6 3 5 1 9 10 2 */