【HDU】4836 The Query on the Tree dfs+線段樹
阿新 • • 發佈:2019-01-26
題目分析:首先如果不換根的話,就可以用dfs求時間戳+樹狀陣列維護即可。
現在多了換根操作,我們該怎麼處理?
首先因為換根並不會改變樹的結構,所以我們依舊dfs出一棵樹來。
對於修改,我們改變該點在樹狀陣列上對應位置的值即可。
對於換根,我們直接將root置為要換的節點。
對於查詢,如果root就等於x,直接返回所有節點的和tot_val即可;
如果root和查詢的節點x的lca不為x,即root不是x的子樹,顯然我們直接對【in[x] , ou[x]】求區間和sum即可;
如果root和x的lca為x,那麼我們找到x的子節點y使得y在root到x路徑上,答案即tot_val - sum。
程式碼如下:
#include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std ; typedef long long LL ; #pragma comment(linker, "/STACK:16777216") #define Log( i , a , b ) for ( int i = a ; ( 1 << i ) <= b ; ++ i ) #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 travel( e , H , u ) for ( Edge* e = H[u] ; e ; e = e -> next ) #define clr( a , x ) memset ( a , x , sizeof a ) #define cpy( a , x ) memcpy ( a , x , sizeof a ) const int LOGN = 15 ; const int MAXN = 10005 ; const int MAXE = 20005 ; struct Edge { int v ; Edge* next ; } E[MAXE] , *H[MAXN] , *edge ; int T[MAXN] ; int in[MAXN] , ou[MAXN] , dfs_clock ; int inc[MAXN][LOGN] ; int dep[MAXN] ; int val[MAXN] ; int tot_val ; int root ; int n , q ; void clear () { edge = E ; root = 1 ; tot_val = 0 ; dfs_clock = 0 ; clr ( H , 0 ) ; clr ( T , 0 ) ; } void addedge ( int u , int v ) { edge -> v = v ; edge -> next = H[u] ; H[u] = edge ++ ; } void dfs ( int u ) { in[u] = ++ dfs_clock ; travel ( e , H , u ) { int v = e -> v ; if ( v != inc[u][0] ) { inc[v][0] = u ; dep[v] = dep[u] + 1 ; dfs ( v ) ; } } ou[u] = dfs_clock ; } void preProcess () { dfs ( 1 ) ; For ( i , 1 , n ) Log ( j , 1 , n ) inc[i][j] = 0 ; Log ( j , 1 , n ) For ( i , 1 , n ) if ( inc[i][j - 1] ) inc[i][j] = inc[inc[i][j - 1]][j - 1] ; } int lca1 ( int x , int y ) { if ( dep[x] < dep[y] ) swap ( x , y ) ; int log = 0 ; Log ( i , 1 , dep[x] ) ++ log ; rev ( i , log , 0 ) if ( dep[x] - ( 1 << i ) >= dep[y] ) x = inc[x][i] ; if ( x == y ) return y ; rev ( i , log , 0 ) if ( inc[x][i] && inc[x][i] != inc[y][i] ) { x = inc[x][i] ; y = inc[y][i] ; } return inc[x][0] ; } int lca2 ( int x , int depth ) { int log = 0 ; Log ( i , 1 , dep[x] ) ++ log ; rev ( i , log , 0 ) if ( dep[x] - ( 1 << i ) >= depth ) x = inc[x][i] ; return x ; } void add ( int x , int v ) { for ( ; x <= n ; x += x & -x ) T[x] += v ; } int sum ( int x , int ans = 0 ) { for ( ; x ; x -= x & -x ) ans += T[x] ; return ans ; } void scanf ( int& x , char c = 0 ) { while ( ( c = getchar () ) < '0' || c > '9' ) ; x = c - '0' ; while ( ( c = getchar () ) >= '0' && c <= '9' ) x = x * 10 + c - '0' ; } void solve () { int x , y ; char op ; clear () ; scanf ( n ) ; rep ( i , 1 , n ) { scanf ( x ) , scanf ( y ) ; addedge ( x , y ) ; addedge ( y , x ) ; } preProcess () ; For ( i , 1 , n ) { scanf ( val[i] ) ; add ( in[i] , val[i] ) ; tot_val += val[i] ; } scanf ( q ) ; while ( q -- ) { op = getchar () ; scanf ( x ) ; if ( op == 'C' ) { scanf ( y ) ; tot_val += y - val[x] ; add ( in[x] , -val[x] ) ; add ( in[x] , val[x] = y ) ; } else if ( op == 'Q' ) { if ( root == x ) printf ( "%d\n" , tot_val ) ; else { int a = lca1 ( root , x ) ; if ( a != x ) printf ( "%d\n" , sum ( ou[x] ) - sum ( in[x] - 1 ) ) ; else { int b = lca2 ( root , dep[x] + 1 ) ; printf ( "%d\n" , tot_val - sum ( ou[b] ) + sum ( in[b] - 1 ) ) ; } } } else root = x ; } } int main () { int T , cas = 0 ; scanf ( "%d" , &T ) ; while ( T -- ) { printf ( "Case #%d:\n" , ++ cas ) ; solve () ; } return 0 ; }