1. 程式人生 > >【HDU】4836 The Query on the Tree dfs+線段樹

【HDU】4836 The Query on the Tree dfs+線段樹

題目分析:首先如果不換根的話,就可以用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 ;
}