【POJ】3580 SuperMemo 【splay】
2014-08-15 17:43
477 查看
传送门:【POJ】3580 SuperMemo
题目分析:这道题又——调了一天。。。。真是伤啊。。。。
题目不难,都是splay的基本操作,但是由于我代码能力太差各种写搓。。。尤其今天是败在了删除一个节点上,写了数据生成器才发现remove函数彻底写错了。。。。T u T。。一天就这么过去了。。。
这题没给数据范围,其实int就够了,最大值别设太小就行,一般0x3f3f3f3f够了。还有就是其中将一个区间循环的操作需要注意,不仅循环次数很大需要取模,而且还可能给你来负的!
大概讲解一下6个操作的解决方法:
1.ADD 旋转出区间[L,R](即l-1为根结点,r+1为根结点的右节点,此时区间[L,R],为根结点的右节点的子树),然后对子树的根打上标记。和线段树一样,lazy标记呗。
2.REVERSE 同样旋转出区间[L,R],对[L,R]子树的根的标记取反。
3.REVOLVE 其实就是得到一个取模后的T,然后将[L,R-T]插入到已经删除了这个区间的位置L-1+T的右端,即将L-1+T旋转到根,再将L+T旋转为根的右节点,然后将区间[L,R-T]变为根结点的右节点的左节点即可。
4.INSERT 新建一个节点,然后用类似操作3的方法插入。
5.DELETE 将要删除的数所在的位置pos作为一个区间[pos,pos]旋转出来,然后像切树一样切掉就行了。
6.MIN 只要在pushup以及pushdown维护一下子树的最小值,然后用旋转区间的方法旋转出区间[L,R],直接返回这个区间子树的根结点的信息即可。
代码如下:
为方便调试,另附小数据生成器:
题目分析:这道题又——调了一天。。。。真是伤啊。。。。
题目不难,都是splay的基本操作,但是由于我代码能力太差各种写搓。。。尤其今天是败在了删除一个节点上,写了数据生成器才发现remove函数彻底写错了。。。。T u T。。一天就这么过去了。。。
这题没给数据范围,其实int就够了,最大值别设太小就行,一般0x3f3f3f3f够了。还有就是其中将一个区间循环的操作需要注意,不仅循环次数很大需要取模,而且还可能给你来负的!
大概讲解一下6个操作的解决方法:
1.ADD 旋转出区间[L,R](即l-1为根结点,r+1为根结点的右节点,此时区间[L,R],为根结点的右节点的子树),然后对子树的根打上标记。和线段树一样,lazy标记呗。
2.REVERSE 同样旋转出区间[L,R],对[L,R]子树的根的标记取反。
3.REVOLVE 其实就是得到一个取模后的T,然后将[L,R-T]插入到已经删除了这个区间的位置L-1+T的右端,即将L-1+T旋转到根,再将L+T旋转为根的右节点,然后将区间[L,R-T]变为根结点的右节点的左节点即可。
4.INSERT 新建一个节点,然后用类似操作3的方法插入。
5.DELETE 将要删除的数所在的位置pos作为一个区间[pos,pos]旋转出来,然后像切树一样切掉就行了。
6.MIN 只要在pushup以及pushdown维护一下子树的最小值,然后用旋转区间的方法旋转出区间[L,R],直接返回这个区间子树的根结点的信息即可。
代码如下:
/* 本算法需要两个虚拟节点:0、n+1 算法基本函数功能: clear () 初始化 newNode ( int v , SplayNode* f ) 新建节点并赋值v以及令其父亲为f init ( int n , int num[] ) 初始化 rotate ( SplayNode* o , int d ) 左右旋,d=0表示左旋,d=1表示右旋 splay ( SplayNode* o , SplayNode* f ) 伸展o直到o的父节点为f为止 select ( int k , SplayNode* f ) 选择第k个元素并将其旋转到其父节点为f为止(不计虚拟节点) SplayNode* get ( int l , int r ) 返回区间[l,r]为子树的根:即将l-1旋转到根,将r+1旋转为l-1的右节点 void split ( int l , int r , SplayNode* &o ) 将区间[l,r]剪切到o void merge ( int pos , SplayNode* o ) 将o插入到pos后端:即将pos旋转到根,pos+1旋转为pos的右节点 void cut ( int l , int r , int pos ) 剪出区间[l,r]并插入到pos的后端 void reverse ( int l , int r ) 翻转区间[l,r] void revolve ( int l , int r , int T ) 剪出区间[l,R-T],然后在删除了该区间后的位置l-1+T后端插入该区间 void getMin ( int l , int r ) 返回区间[l,r]最小值 void add ( int l , int r , int v ) 对区间[l,r]内的所有数加上v void insert ( int pos , int v ) 在pos的后端插入值为v的新节点 void remove ( int pos ) 删除位置为pos的节点 void print ( SplayNode* o ) 输出信息 */ #include <cstdio> #include <cstring> #include <algorithm> using namespace std ; #define REP( i , a , b ) for ( int i = a ; i < b ; ++ i ) #define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i ) #define REV( i , a , b ) for ( int i = a ; i >= b ; -- i ) #define CLR( a , x ) memset ( a , x , sizeof a ) const int MAXN = 233333 ; const int INF = 0x3f3f3f3f ; struct SplayNode { SplayNode *c[2] , *f ; int s ; int v ; bool rev ; int addv ; int minv ; void add ( int val ) { v += val ; addv += val ; minv = min ( v , minv ) ; } void pushup () { s = c[0] -> s + c[1] -> s + 1 ; minv = min ( c[0] -> minv , c[1] -> minv ) ; minv = min ( minv , v ) ; } void pushdown () { if ( rev ) { swap ( c[0] , c[1] ) ; c[0] -> rev ^= 1 ; c[1] -> rev ^= 1 ; rev = 0 ; } if ( addv ) { if ( ~c[0] -> v ) { c[0] -> v += addv ; c[0] -> addv += addv ; c[0] -> minv += addv ; } if ( ~c[1] -> v ) { c[1] -> v += addv ; c[1] -> addv += addv ; c[1] -> minv += addv ; } addv = 0 ; } } } Tnull , *null = &Tnull ; struct Splay { SplayNode node[MAXN] , *cur ; SplayNode *root ; void clear () { null -> s = 0 ; null -> v = -1 ; null -> minv = INF ; cur = node ; root = null ; } SplayNode* newNode ( int v , SplayNode* f ) { cur -> v = v ; cur -> s = 1 ; cur -> rev = 0 ; cur -> addv = 0 ; cur -> minv = v ; cur -> c[0] = cur -> c[1] = null ; cur -> f = f ; return cur ++ ; } void rotate ( SplayNode* o , bool d ) { SplayNode* p = o -> f ; p -> pushdown () ; o -> pushdown () ; p -> c[!d] = o -> c[d] ; if ( o -> c[d] != null ) o -> c[d] -> f = p ; o -> f = p -> f ; if ( p -> f != null ) { if ( p == p -> f -> c[0] ) p -> f -> c[0] = o ; else p -> f -> c[1] = o ; } o -> c[d] = p ; p -> f = o ; p -> pushup () ; if ( root == p ) root = o ; } void splay ( SplayNode* o , SplayNode* f ) { while ( o -> f != f ) { SplayNode* p = o -> f ; if ( p -> f == f ) { if ( o == p -> c[0] ) rotate ( o , 1 ) ; else rotate ( o , 0 ) ; } else { if ( p == p -> f -> c[0] ) { if ( o == p -> c[0] ) rotate ( p , 1 ) , rotate ( o , 1 ) ; else rotate ( o , 0 ) , rotate ( o , 1 ) ; } else { if ( o == p -> c[1] ) rotate ( p , 0 ) , rotate ( o , 0 ) ; else rotate ( o , 1 ) , rotate ( o , 0 ) ; } } } o -> pushup () ; } void select ( int k , SplayNode* f ) { SplayNode* o = root ; ++ k ; while ( o != null ) { o -> pushdown () ; int s = o -> c[0] -> s ; if ( k == s + 1 ) break ; if ( k <= s ) o = o -> c[0] ; else { k -= s + 1 ; o = o -> c[1] ; } } splay ( o , f ) ; } SplayNode* get ( int l , int r ) { select ( l - 1 , null ) ; select ( r + 1 , root ) ; return root -> c[1] -> c[0] ; } void split ( int l , int r , SplayNode* &o ) { o = get ( l , r ) ; o -> f = null ; root -> c[1] -> c[0] = null ; root -> c[1] -> pushup () ; root -> pushup () ; } void merge ( int pos , SplayNode* o ) { get ( pos + 1 , pos ) ; o -> f = root -> c[1] ; root -> c[1] -> c[0] = o ; root -> c[1] -> pushup () ; root -> pushup () ; splay ( o , null ) ; } void cut ( int l , int r , int pos ) { SplayNode* o ; split ( l , r , o ) ; merge ( pos , o ) ; } void reverse ( int l , int r ) { SplayNode* o = get ( l , r ) ; o -> rev ^= 1 ; splay ( o , null ) ; } void revolve ( int l , int r , int T ) { cut ( l , r - T , l - 1 + T ) ; } void add ( int l , int r , int v ) { SplayNode* o = get ( l , r ) ; o -> add ( v ) ; splay ( o , null ) ; } void insert ( int pos , int v ) { get ( pos + 1 , pos ) ; SplayNode* o = newNode ( v , root -> c[1] ) ; root -> c[1] -> c[0] = o ; root -> c[1] -> pushup () ; root -> pushup () ; } void remove ( int pos ) { get ( pos , pos ) ; root -> c[1] -> c[0] = null ; root -> c[1] -> pushup () ; root -> pushup () ; } int getMin ( int l , int r ) { SplayNode* o = get ( l , r ) ; return o -> minv ; } void init ( int n , int num[] ) { clear () ; FOR ( i , 0 , n + 1 ) { SplayNode* o = newNode ( num[i] , null ) ; o -> c[0] = root ; root -> f = o ; root = o ; root -> pushup () ; } } void print ( SplayNode* o ) { if ( o -> c[0] != null ) print ( o -> c[0] ) ; printf ( "左儿子v:%d----自己v:%d----右儿子v:%d\n" , o -> c[0] -> v , o -> v , o -> c[1] -> v ) ; if ( o -> c[1] != null ) print ( o -> c[1] ) ; } } T ; int n , p ; int num[MAXN] ; char s[10] ; int x , y , v ; void solve () { num[0] = num[n + 1] = INF ; FOR ( i , 1 , n ) scanf ( "%d" , &num[i] ) ; T.init ( n , num ) ; scanf ( "%d" , &p ) ; while ( p -- ) { scanf ( "%s" , s ) ; if ( s[0] == 'A' ) { scanf ( "%d%d%d" , &x , &y , &v ) ; T.add ( x , y , v ) ; } else if ( s[0] == 'R' && s[3] == 'E' ) { scanf ( "%d%d" , &x , &y ) ; T.reverse ( x , y ) ; } else if ( s[0] == 'R' && s[3] == 'O' ) { scanf ( "%d%d%d" , &x , &y , &v ) ; v %= y - x + 1 ; if ( v < 0 ) v += y - x + 1 ; T.revolve ( x , y , v ) ; } else if ( s[0] == 'I' ) { scanf ( "%d%d" , &x , &v ) ; T.insert ( x , v ) ; } else if ( s[0] == 'D' ) { scanf ( "%d" , &x ) ; T.remove ( x ) ; } else { scanf ( "%d%d" , &x , &y ) ; printf ( "%d\n" , T.getMin ( x , y ) ) ; } } } int main () { while ( ~scanf ( "%d" , &n ) ) solve () ; return 0 ; }
为方便调试,另附小数据生成器:
#include <cstdio> #include <cstring> #include <algorithm> #include <stdlib.h> #include <time.h> using namespace std ; #define REP( i , a , b ) for ( int i = a ; i < b ; ++ i ) #define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i ) #define REV( i , a , b ) for ( int i = a ; i >= b ; -- i ) #define CLR( a , x ) memset ( a , x , sizeof a ) const int R = 100 ; int main () { srand ( time ( NULL ) ) ; int n , x , y , q , ch ; freopen ( "poj3580.txt" , "w" , stdout ) ; REP ( Q , 0 , 10 ) { n = rand () % 100 + 1 ; printf ( "%d\n" , n ) ; REP ( i , 0 , n ) printf ( "%d " , rand () % R + 1 ) ; printf ( "\n" ) ; q = rand () % 100 + 1 ; printf ( "%d\n" , q ) ; while ( q -- ) { ch = rand () % 6 + 1 ; if ( ch == 1 ) { x = rand () % n + 1 ; while ( ( y = rand () % n + 1 ) < x ) ; printf ( "ADD %d %d %d\n" , x , y , rand () % R + 1 ) ; } else if ( ch == 2 ) { x = rand () % n + 1 ; while ( ( y = rand () % n + 1 ) < x ) ; printf ( "REVERSE %d %d\n" , x , y ) ; } else if ( ch == 3 ) { x = rand () % n + 1 ; while ( ( y = rand () % n + 1 ) < x ) ; printf ( "REVOLVE %d %d %d\n" , x , y , rand () % R + 1 ) ; } else if ( ch == 4 ) { printf ( "INSERT %d %d\n" , rand () % n + 1 , rand () % R + 1 ) ; ++ n ; } else if ( ch == 5 ) { printf ( "DELETE %d\n" , rand () % n + 1 ) ; -- n ; } else { x = rand () % n + 1 ; while ( ( y = rand () % n + 1 ) < x ) ; printf ( "MIN %d %d\n" , x , y ) ; } } } return 0 ; }
相关文章推荐
- POJ 3580——SuperMemo(Splay树,经典题)
- BZOJ 1895 & POJ 3580 supermemo [SPLAY]【数据结构】
- POJ 3580 SuperMemo (Splay 区间更新、翻转、循环右移,插入,删除,查询)
- POJ-3580-SuperMemo(splay的各种操作)
- POJ 3580 SuperMemo [Splay]
- Splay树(多操作)——POJ 3580 SuperMemo
- Splay伸展树学习小记 Poj 3580 SuperMemo
- POJ-3580 SuperMemo(Splay树)
- POJ 3580 SuperMemo(Splay)
- BZOJ 1895 & POJ 3580 supermemo (splay)
- POJ 3580 SuperMemo (Splay)
- POJ 3580 SuperMemo(splay)
- POJ 3580(SuperMemo-Splay区间加)[template:Splay V2]
- POJ 3580 SuperMemo(Splay模板)
- POJ 3580 SuperMemo(Splay)
- POJ 3580 SuperMemo (Splay)
- 平衡树(Splay):Splaytree POJ 3580 SuperMemo
- Splay树(多操作)——POJ 3580 SuperMemo
- POJ 3580 SuperMemo (Splay各种区间操作)
- POJ 3580 SuperMemo(Splay树)