BZOJ-3065: 带插入区间K小值(替罪羊树套权值线段树)

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=3065

刚开始想用splay维护,但是死活想不出旋转时维护信息的方法,果断放弃,然后又打算用分块的思想,插入了sqrt(m)个数后再次分治重建树。ORZ了VFK的博客之后才发现,貌似带根号的会TLE,果断放弃。对于这道题,虽然依赖于旋转的平衡树无法达到要求,但是不依赖或者是依赖旋转程度很小(比如treap)的重量平衡树可以满足套上线段树这个要求,所以我选择了相对比较好写的替罪羊树,然后每次询问就在树上找到相应的一组节点,沿着权值线段树走就好啦。。。

代码:

b3119313b07eca801dc0e183932397dda0448389.jpg.png
#include 

#include 

#include 

#include 

 

using namespace std ;

 

#define MAXN 80010

#define MAXL 70010

#define a 0.80

#define inf 0x7fffffff

#define check( ch ) ( ch >= '0' && ch <= '9' )

#define L( t ) Left[ t ]

#define R( t ) Right[ t ]

#define K( t ) Key[ t ]

#define S( t ) Size[ t ]

#define T( t ) Tree[ t ]

 

void getint( int &t ) {

    int ch ; for ( ch = getchar(  ) ; ! check( ch ) ; ch = getchar(  ) ) ;

    t = ch - '0' ;

    for ( ch = getchar(  ) ; check( ch ) ; ch = getchar(  ) ) t *= 10 , t += ch - '0' ;

}

  

void putint( int t ) {

    if ( ! t ) putchar( '0' ) ; else {

        int ans[ 20 ] ; ans[ 0 ] = 0 ;

        for ( ; t ; t /= 10 ) ans[ ++ ans[ 0 ] ] = t % 10 ;

        for ( ; ans[ 0 ] ; ans[ 0 ] -- ) putchar( '0' + ans[ ans[ 0 ] ] ) ;

    }

    putchar( '\n' ) ;

}

 

struct node {

    node *left , *right ;

    int sum ;

    node (  ) {

        sum = 0 ;

        left = right = NULL ;

    }

} *blank = new( node ) ;

 

void Add( int l , int r , int k , node* &t) {

    if ( t == blank ) t = new( node ) , t -> left = t -> right = blank ;

    t -> sum ++ ;

    if ( l == r ) return ;

    int mid = ( l + r ) >> 1 ;

    if ( k <= mid ) Add( l , mid , k , t -> left ) ; else Add( mid + 1 , r , k , t -> right ) ;

}

 

void Del( int l , int r , int k , node* &t) {

    t -> sum -- ;

    if ( l == r ) return ;

    int mid = ( l + r ) >> 1 ;

    if ( k <= mid ) Del( l , mid , k , t -> left ) ; else Del( mid + 1 , r , k , t -> right ) ;

}

 

int Left[ MAXN ] , Right[ MAXN ] , Key[ MAXN ] , Size[ MAXN ] , V = 0 , roof ;

node *Tree[ MAXN ] ;

 

int n , m , w[ MAXN ] ;

int dfn[ MAXN ] , Index ;

 

void build( int l , int r , int &t ) {

    if ( l > r ) {

        t = 0 ; return ;

    }

    int mid = ( l + r ) >> 1 ;

    t = dfn[ mid ] ;

    T( t ) = blank , S( t ) = r - l + 1 ;

    for ( int i = l ; i <= r ; i ++ ) {

        Add( 0 , MAXL , K( dfn[ i ] ) , T( t ) ) ;

    }

    build( l , mid - 1 , L( t ) ) , build( mid + 1 , r , R( t ) ) ;

}

 

node *Range[ MAXN ] ;

int P[ MAXN ] , nr , np ;

 

void ranging( int l , int r , int t ) {

    if ( r < S( L( t ) ) ) ranging( l , r , L( t ) )

    ; else if ( l > S( L( t ) ) ) ranging( l - S( L( t ) ) - 1 , r - S( L( t ) ) - 1 , R( t ) )

    ; else {

        if ( ! l && r == S( t ) - 1 ) Range[ ++ nr ] = T( t )

        ; else {

            P[ ++ np ] = K( t ) ;

            if ( l < S( L( t ) ) ) ranging( l , S( L( t ) ) - 1 , L( t ) ) ;

            if ( r > S( L( t ) ) ) ranging( 0 , r - S( L( t ) ) - 1 , R( t ) ) ;

        }

    }

}

 

void getrange( int l , int r ) {

    nr = np = 0 ;

    ranging( l - 1 , r - 1 , roof ) ;

}

 

void dfs( int t ) {

    if ( L( t ) ) dfs( L( t ) ) ;

    dfn[ ++ Index ] = t ;

    if ( R( t ) ) dfs( R( t ) ) ;

}

 

void Recycle( node *t ) {

    if ( ! t -> sum ) return ;

    if ( t -> left ) Recycle( t -> left ) ;

    if ( t -> right ) Recycle( t -> right ) ;

    delete( t ) ;

}

 

void rebuild( int &t ) {

    Index = 0 ;

    dfs( t ) ;

    for ( int i = 0 ; i ++ < Index ; ) Recycle( T( dfn[ i ] ) ) ;

    build( 1 , Index , t ) ;

}

 

bool Insert( int r , int k , int h , int &t ) {

    if ( ! t ) {

        t = ++ V ;

        S( t ) = 1 , K( t ) = k , L( t ) = R( t ) = 0 , T( t ) = blank ;

        Add( 0 , MAXL , k , T( t ) ) ;

        return h > log( V ) / log( 1 / a ) ;

    }

    bool flag ;

    if ( r <= S( L( t ) ) ) flag = Insert( r , k , h + 1 , L( t ) )

    ; else flag = Insert( r - S( L( t ) ) - 1 , k , h + 1 , R( t ) ) ;

    S( t ) = S( L( t ) ) + S( R( t ) ) + 1 ;

    Add( 0 , MAXL , k , T( t ) ) ;

    if ( flag && ( max( S( L( t ) ) , S( R( t ) ) ) > a * S( t ) ) ) {

        rebuild( t ) ;

        return false ;

    }

    return flag ;

}

 

int Change( int r , int k , int t ) {

    if ( S( L( t ) ) == r ) {

        Del( 0 , MAXL , K( t ) , T( t ) ) ;

        int v = K( t ) ; K( t ) = k ;

        Add( 0 , MAXL , k , T( t ) ) ;

        return v ;

    }

    int v ;

    if ( r < S( L( t ) ) ) v = Change( r , k , L( t ) )

    ; else v = Change( r - S( L( t ) ) - 1 , k , R( t ) ) ;

    Del( 0 , MAXL , v , T( t ) ) ;

    Add( 0 , MAXL , k , T( t ) ) ;

    return v ;

}

 

int Query( int l , int r , int k ) {

    k -- ;

    getrange( l , r ) ;

    int L = 0 , R = MAXL ;

    while ( L < R ) {

        int mid = ( L + R ) >> 1 , sum = 0 ;

        for ( int i = 0 ; i ++ < nr ; ) sum += Range[ i ] -> left -> sum ;

        for ( int i = 0 ; i ++ < np ; ) sum += ( P[ i ] >= L && P[ i ] <= mid ) ? 1 : 0 ;

        if ( k < sum ) {

            for ( int i = 0 ; i ++ < nr ; ) Range[ i ] = Range[ i ] -> left ;

            R = mid ;

        } else {

            k -= sum ;

            for ( int i = 0 ; i ++ < nr ; ) Range[ i ] = Range[ i ] -> right ;

            L = mid + 1 ;

        }

    }

    return L ;

}

 

int main(  ) {

    blank -> left = blank -> right = blank ;

    getint( n ) ; for ( int i = 0 ; i ++ < n ; ) getint( w[ i ] ) ;

    L( 0 ) = R( 0 ) = S( 0 ) = 0 ;

    for ( int i = 0 ; i ++ < n ; ) {

        dfn[ i ] = ++ V ; K( V ) = w[ i ] ;

    }

    build( 1 , n , roof ) ;

    getint( m ) ;

    int last = 0 ;

    while ( m -- ) {

        int ch ; for ( ch = getchar(  ) ; ! ( ch >= 'A' && ch <= 'Z' ) ; ch = getchar(  ) ) ;

        if ( ch == 'Q' ) {

            int x , y , k ; getint( x ) , getint( y ) , getint( k ) ;

            x ^= last , y ^= last , k ^= last ;

            putint( last = Query( x , y , k ) ) ;

        } else if ( ch == 'I' ) {

            int x , y ; getint( x ) , getint( y ) ;

            x ^= last , y ^= last ;

            Insert( x - 1 , y , 0 , roof ) ;

        } else {

            int x , y ; getint( x ) , getint( y ) ;

            x ^= last , y ^= last ;

            Change( x - 1 , y , roof ) ;

        }

    }

    return 0 ;

}

你可能感兴趣的:(BZOJ-3065: 带插入区间K小值(替罪羊树套权值线段树))