【HDU】4836 The Query on the Tree dfs+线段树

本文介绍了一种使用深度优先搜索(DFS)结合树状数组来解决涉及换根操作的问题的方法。通过预处理及巧妙利用树的性质,实现了高效查询与更新。文章详细解释了算法思路,并提供了完整的实现代码。

传送门:【HDU】4836 The Query on the Tree


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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值