天天看點

BZOJ2002 彈飛綿羊 LCT維護size求深度大家都很強, 可與之共勉。

大家都很強, 可與之共勉。

題意:

一共n個位置,每個位置一個屬性k[i],表示在i位置會被瞬間轉移到i+k[i](然後又依次轉移)。問從一個點開始多少次會出界。并且支援修改k[i]。

題解:

把i向i+k[i]連邊,若i+k[i]出界就向外面的總根連邊。詢問就是求深度。

把一個節點splay到根之後(前提是根是原樹最開始的根),左邊一定是比它淺的(且一定是它到原樹根的一條鍊(因為access操作)),是以左兒子的size就是它的深度(根的深度為0)。

居然以前要寫一天的LCT半個小時就寫好了,要不是BZOJ的編譯器棧空間不夠就1A了

/**************************************************************
    Problem: 2002
    User: Lazer2001
    Language: C++
    Result: Accepted
    Time:1988 ms
    Memory:22324 kb
****************************************************************/

# include <bits/stdc++.h>

inline int read ( )  {
    register int x, c ;
    while ( isspace ( c = getchar ( ) ) ) ;
    for ( x = - + c ; isdigit ( c = getchar ( ) ) ; ( x *=  ) += c -  ) ;
    return x ;
}

template < class T >  inline T min ( const T& a, const T& b )  {  return a < b ? a : b ;  }
template < class T >  inline void swap ( T& a, T& b )  {  T c ( a ) ; a = b, b = c ;  }

# define N 200010

class LinkCutTree  {
    private :
        struct node  {
            int siz ;
            bool rev_flag ;
            node *ch [], *fa ;
            inline void update ( )  {
                siz = ch [] -> siz + ch [] -> siz +  ;
            }
        } pool [N << ], *root [N], *null ;

        int n ;

        inline void push_down ( node*& p )  {
            if ( p -> rev_flag )  {
                swap ( p -> ch [], p -> ch [] ) ;
                if ( p -> ch [] != null )  p -> ch [] -> rev_flag ^=  ;
                if ( p -> ch [] != null )  p -> ch [] -> rev_flag ^=  ;
                p -> rev_flag =  ;
            }
        }

        inline node* newnode ( node*& fa )  {
            static node* tp ( pool +  ) ;
            tp -> siz =  ; tp -> rev_flag =  ;
            return tp -> fa = fa, tp -> ch [] = tp -> ch [] = null, tp ++ ;
        }

    # define isroot( p )  ( p -> fa == null || ( p -> fa -> ch [0] != p && p -> fa -> ch [1] != p ) )
    # define isrs( p )  ( p == p -> fa -> ch [1] )
        inline void rotate ( node* p )  {
            if ( p == null || isroot ( p ) )  return ;
            bool d ( isrs ( p ) ) ;
            node* par = p -> fa ;
            par -> ch [d] = p -> ch [! d] ;
            if ( p -> ch [! d] != null )  p -> ch [! d] -> fa = par ;
            if ( ! isroot ( par ) )  par -> fa -> ch [isrs ( par )] = p ; // !isroot(par)
            p -> fa = par -> fa ;
            par -> fa = p ;
            p -> ch [! d] = par ;
            par -> update ( ) ; p -> update ( ) ;
        }
        node* st [N << ] ;
        inline void splay ( node* p )  {
            if ( p == null )  return ;
        //  static node* st [N << 1] ; static int tp ( 0 ) ; RE!!!!
            int tp ;
            st [tp = ] = p ;
            for ( node* t = p ; ! isroot ( t ) ; t = t -> fa )  st [++ tp] = t -> fa ;
            while ( tp )  push_down ( st [tp --] ) ;
            while ( ! isroot ( p ) )  {
                if ( isrs ( p ) == isrs ( p -> fa ) && ! isroot ( p -> fa ) )  rotate ( p -> fa ) ;
                rotate ( p ) ;
            }
        }

    # undef isrs
    # undef isroot
        inline void access ( node* p )  {
            for ( node* pre = null ; p != null ; pre = p, p = p -> fa )
                splay ( p ), p -> ch [] = pre, p -> update ( ) ;
        }
        inline void makeroot ( node* p )  {
            access ( p ) ;  splay ( p ) ; p -> rev_flag ^=  ;
        }
        inline void cut ( node* p, node* q )  {
            makeroot ( p ) ; access ( q ) ; splay ( q ) ;
            q -> ch [] = null, p -> fa = null ;
            q -> update ( ) ;
        }
        inline void link ( node* p, node* q )  {
            makeroot ( p ) ; p -> fa = q ;
        }
        inline int dep ( node* p )  {
            makeroot ( root [n + ] ) ;
            access ( p ) ;
            splay ( p ) ;
            return p -> ch [] -> siz ;
        }
    public :
        LinkCutTree ( )  {
            null = pool ;
            null -> siz =  ; null -> rev_flag =  ;
            null -> ch [] = null -> ch [] = null -> fa = null ;
        }
        LinkCutTree ( int n ) : n ( n ) {
            null = pool ;
            null -> siz =  ; null -> rev_flag =  ;
            null -> ch [] = null -> ch [] = null -> fa = null ;
            for ( register int i =  ; i <= n +  ; ++ i )  root [i] = newnode ( null ) ;
        }
        inline void access ( int u )  {  access ( root [u] ) ;  }
        inline void makeroot ( int u )  {  makeroot ( root [u] ) ;  }
        inline void cut ( int u, int v )  {  cut ( root [u], root [v] ) ;   }
        inline void link ( int u, int v )  {  link ( root [u], root [v] ) ;  }
        inline int dep ( int u )  {  return dep ( root [u] ) ;  }
} T ;

int a [N] ;

# undef N

//# define ZJC_LOCAL

int main ( )  {
# ifdef ZJC_LOCAL
    freopen ( "in.txt", "r", stdin ) ;
    freopen ( "out.txt", "w", stdout ) ;
# endif
    int n ( read ( ) ) ;
    T = LinkCutTree ( n ) ;
    for ( int i =  ; i <= n ; ++ i )  {
        T.link ( i, min ( i + ( a [i] = read ( ) ), n +  ) ) ;
    }
    for ( int m = read ( ) ; m ; -- m )  {
        int opt ( read ( ) ) ;
        if ( opt ==  )  {
            int u ( read ( ) +  ) ; // + 1 !!!
            printf ( "%d\n", T.dep ( u ) ) ;
        }  else  {
            int u ( read ( ) +  ), k ( read ( ) ) ;
            T.cut ( u, min ( u + a [u], n +  ) ) ;
            T.link ( u, min ( u + ( a [u] = k ), n +  ) ) ;
        }
    }
}
           

資料生成器(随機)

# include <bits/stdc++.h>

inline int rd ( )  {
    return rand ( ) <<  | rand ( ) ;
}

int main ( )  {
    srand ( time (  ) ) ;
    freopen ( "in.txt", "w", stdout ) ;
    int n = , m =  ;
    printf ( "%d\n", n ) ;
    for ( int i =  ; i <= n ; ++ i )  printf ( "%d ", rd ( ) %  +  ) ;
    printf ( "\n%d\n", m ) ;
    while ( m -- )  {
        int opt ( rand ( ) %  +  ) ;
        printf ( "%d ", opt ) ;
        if ( opt ==  )  {
            printf ( "%d\n", rd ( ) % n ) ;
        }  else  {
            printf ( "%d %d\n", rd ( ) % n, rd ( ) %  +  ) ;
        }
    }
}
           

震驚!!!分塊比LCT跑得快!!!

這莫非是根号算法的勝利?(滑稽)

短且好些好調,1A.

處理出每個點跳出那個塊的步數,然後再處理出每個點跳出塊到下一個地點是什麼地方(可轉移)。

倒着處理。

每次修改隻會對塊内産生影響(很好想)。

于是就很簡單了。

複雜度O((n+m)sqrt(n))

比log快,LCT自帶大常數……

/**************************************************************
    Problem: 
    User: Lazer2001
    Language: C++
    Result: Accepted
    Time: ms
    Memory: kb
****************************************************************/

# include <bits/stdc++.h>

inline int read ( )  {
    register int x, c ;
    while ( isspace ( c = getchar ( ) ) ) ;
    for ( x = - + c ; isdigit ( c = getchar ( ) ) ; ( x *=  ) += c -  ) ;
    return x ;
}

template < class T >  inline T min ( const T& a, const T& b )  {  return a < b ? a : b ;  }

# define N 200020

int k [N], step [N], nxt [N], pos [N] ;

# undef N

int n ;

inline void solve ( int i )  {
    int pro = min ( i + k [i], n +  ) ;
    if ( pos [i] == pos [pro] )  {
        step [i] = step [pro] +  ;
        nxt [i] = nxt [pro] ;
    }  else  {
        step [i] =  ;
        nxt [i] = pro ;
    }
}

//# define ZJC_LOCAL

int main ( )  {
# ifdef ZJC_LOCAL
    freopen ( "in.txt", "r", stdin ) ;
    freopen ( "out.txt", "w", stdout ) ;
# endif
    n = read ( ) ;
    for ( int i =  ; i <= n ; ++ i )  k [i] = read ( ) ;
    int blk = ( int ) sqrt ( n +  ) ;
    for ( int i =  ; i <= n ; ++ i )  pos [i] = ( i -  ) / blk +  ;
    pos [n + ] = pos [n] +  ; // !!!convenient;
    for ( int i = n ; i >=  ; -- i )  solve ( i ) ;
    for ( int ocnt = read ( ) ; ocnt ; -- ocnt )  {
        int opt ( read ( ) ) ;
        if ( opt ==  )  {
            int ans (  ) ;
            for ( int i = read ( ) +  ; i != n +  ; i = nxt [i] )  ans += step [i] ; // pos +  !!!
            printf ( "%d\n", ans ) ;
        }  else  {
            int p ( read ( ) +  ) ; // pos +  !!!
            k [p] = read ( ) ;
            for ( int i = p ; i >=  ; -- i )  {
                if ( pos [i] != pos [p] )  break ;
                solve ( i ) ;
            }
        }
    }
}