现在的位置: 首页 > 综合 > 正文

【HDU】2586 How far away ? LCA求树上点对最近距离

2017年10月15日 ⁄ 综合 ⁄ 共 4547字 ⁄ 字号 评论关闭

How far away ?

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 4702    Accepted Submission(s): 1765

Problem Description

There are n houses in the village and some bidirectional roads connecting them. Every day peole always like to ask like this "How far is it if I want to go from house A to house B"? Usually it hard to
answer. But luckily int this village the answer is always unique, since the roads are built in the way that there is a unique simple path("simple" means you can't visit a place twice) between every two houses. Yout task is to answer all these curious people.
 


Input

First line is a single integer T(T<=10), indicating the number of test cases.
  For each test case,in the first line there are two numbers n(2<=n<=40000) and m (1<=m<=200),the number of houses and the number of queries. The following n-1 lines each consisting three numbers i,j,k, separated bu a single space, meaning that there is a road
connecting house i and house j,with length k(0<k<=40000).The houses are labeled from 1 to n.
  Next m lines each has distinct integers i and j, you areato answer the distance between house i and house j.
 


Output

For each test case,output m lines. Each line represents the answer of the query. Output a bland line after each test case.
 


Sample Input
2 3 2 1 2 10 3 1 15 1 2 2 3 2 2 1 2 100 1 2 2 1
 


Sample Output
10 25 100 100
 


Source

ECJTU 2009 Spring Contest

传送门:【HDU】2586 How far away ?

题型:LCA求树上两点最近距离。

题目大意:
给你一颗n个点的树,树边有权值k,m次询问,每次问(u,v)路径上的权值和最小的路的最小权值和。( 2 <= n <= 40000 , 1 <= m <= 200 , 0 < k <= 40000)。

题目分析:

不知道是不是在线LCA效率有点低?反正效率一般。

我有两种基于倍增的实现方法(个人感觉第二种更好)。
每种都需要先将无根树转化成有根树。这里我们假设已经转化完成。

一:预处理出每个点i与其2^j级祖先的路径权值和。然后在倍增算法中累加即可。

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

#define REPF( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define REPV( i , a , b ) for ( int i = a ; i >= b ; -- i )
#define LOGF( i , n ) for ( int i = 1 ; ( 1 << i ) < n ; ++ i )
#define EDGE( i , x ) for ( int i = adj[x] ; ~i ; i = edge[i].n )
#define clear( a , x ) memset ( a , x , sizeof a )

const int MAXN = 40005 ;
const int MAXE = 80005 ;
const int LOGN = 20 ;

struct Edge {
	int v , w , n ;
	Edge ( int var = 0 , int cost = 0 , int next = 0 ) :
		v ( var ) , w ( cost ) , n ( next ) {}
} ;

struct LCA {
	Edge edge[MAXE] ;
	int adj[MAXN] , cntE ;
	int deep[MAXN] ;
	int cost[MAXN] ;
	int anc[MAXN][LOGN] ;
	int fa[MAXN] ;
	int totcost[MAXN][LOGN] ;
	int n ;
	
	void init () {
		clear ( adj , -1 ) ;
		cntE = 0 ;
	}
	
	void addedge ( int u , int v , int w ) {
		edge[cntE] = Edge ( v , w , adj[u] ) ;
		adj[u] = cntE ++ ;
		edge[cntE] = Edge ( u , w , adj[v] ) ;
		adj[v] = cntE ++ ;
	}
	
	void dfs ( int u , int p ) {
		EDGE ( i , u ) {
			int v = edge[i].v ;
			if ( v != p ) {
				fa[v] = u ;
				deep[v] = deep[u] + 1 ;
				cost[v] = edge[i].w ;
				dfs( v , u ) ;
			}
		}
	}
	
	void preProcess () {
		REPF ( i , 1 , n ) {
			anc[i][0] = fa[i] , totcost[i][0] = cost[i] ;
			LOGF ( j , n )
				anc[i][j] = -1 ;
		}
		LOGF ( j , n )
			REPF ( i , 1 , n )
				if ( ~anc[i][j - 1] ) {
					int u = anc[i][j - 1] ;
					totcost[i][j] = totcost[i][j - 1] + totcost[u][j - 1] ;
					anc[i][j] = anc[u][j - 1] ;
				}
	}
	
	int query ( int p , int q ) {
		int log = 0 , ans = 0 ;
		if ( deep[p] < deep[q] )
			swap ( p , q ) ;
		LOGF ( i , deep[p] + 1 )
			++ log ;
		REPV ( i , log , 0 )
			if ( deep[p] - ( 1 << i ) >= deep[q] ) {
				ans += totcost[p][i] ;
				p = anc[p][i] ;
			}
		if ( p == q )
			return ans ;
		REPV ( i , log , 0 )
			if ( ~anc[p][i] && anc[p][i] != anc[q][i] ) {
				ans += totcost[p][i] + totcost[q][i] ;
				p = anc[p][i] ;
				q = anc[q][i] ;
			}
		ans += cost[p] + cost[q] ;
		return ans ;
	}
} ;

LCA L ;

void work () {
	int u , v , w , m ;
	scanf ( "%d%d" , &L.n , &m ) ;
	L.init () ;
	REPF ( i , 1 , L.n - 1 ) {
		scanf ( "%d%d%d" , &u , &v , &w ) ;
		L.addedge ( u , v , w ) ;
	}
	L.dfs ( 1 , 0 ) ;
	L.preProcess () ;
	while ( m -- ) {
		scanf ( "%d%d" , &u , &v ) ;
		printf ( "%d\n" , L.query ( u , v ) ) ;
	}
}


int main () {
	int T ;
	scanf ( "%d" , &T ) ;
	while ( T -- )
		work () ;
	return 0 ;
}

二:直接dfs中计算得到每个点i到根结点的路径权值和dist[ i ],设询问的点对为(u,v),则(u,v)的最近公共祖先就是lca(u,v),那么答案ans = dist[ u ] + dist[ v ] - 2 * dist[ lca (u,v) ] 。

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

#define REPF( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define REPV( i , a , b ) for ( int i = a ; i >= b ; -- i )
#define LOGF( i , n ) for ( int i = 1 ; ( 1 << i ) < n ; ++ i )
#define EDGE( i , x ) for ( int i = adj[x] ; ~i ; i = edge[i].n )
#define clear( a , x ) memset ( a , x , sizeof a )

const int MAXN = 40005 ;
const int MAXE = 80005 ;
const int LOGN = 20 ;

struct Edge {
	int v , w , n ;
	Edge ( int var = 0 , int cost = 0 , int next = 0 ) :
		v ( var ) , w ( cost ) , n ( next ) {}
} ;

struct LCA {
	Edge edge[MAXE] ;
	int adj[MAXN] , cntE ;
	int deep[MAXN] ;
	int dist[MAXN] ;
	int anc[MAXN][LOGN] ;
	int fa[MAXN] ;
	int n ;
	
	void init () {
		clear ( adj , -1 ) ;
		cntE = 0 ;
	}
	
	void addedge ( int u , int v , int w ) {
		edge[cntE] = Edge ( v , w , adj[u] ) ;
		adj[u] = cntE ++ ;
		edge[cntE] = Edge ( u , w , adj[v] ) ;
		adj[v] = cntE ++ ;
	}
	
	void dfs ( int u , int p ) {
		EDGE ( i , u ) {
			int v = edge[i].v ;
			if ( v != p ) {
				fa[v] = u ;
				deep[v] = deep[u] + 1 ;
				dist[v] = dist[u] + edge[i].w ;
				dfs( v , u ) ;
			}
		}
	}
	
	void preProcess () {
		REPF ( i , 1 , n ) {
			anc[i][0] = fa[i] ;
			LOGF ( j , n )
				anc[i][j] = -1 ;
		}
		LOGF ( j , n )
			REPF ( i , 1 , n )
				if ( ~anc[i][j - 1] ) {
					int u = anc[i][j - 1] ;
					anc[i][j] = anc[u][j - 1] ;
				}
	}
	
	int query ( int p , int q ) {
		int log = 0 ;
		if ( deep[p] < deep[q] )
			swap ( p , q ) ;
		LOGF ( i , deep[p] + 1 )
			++ log ;
		REPV ( i , log , 0 )
			if ( deep[p] - ( 1 << i ) >= deep[q] ) {
				p = anc[p][i] ;
			}
		if ( p == q )
			return p ;
		REPV ( i , log , 0 )
			if ( ~anc[p][i] && anc[p][i] != anc[q][i] ) {
				p = anc[p][i] ;
				q = anc[q][i] ;
			}
		return fa[p] ;
	}
} ;

LCA L ;

void work () {
	int u , v , w , m ;
	scanf ( "%d%d" , &L.n , &m ) ;
	L.init () ;
	REPF ( i , 1 , L.n - 1 ) {
		scanf ( "%d%d%d" , &u , &v , &w ) ;
		L.addedge ( u , v , w ) ;
	}
	L.dist[1] = 0 ;
	L.dfs ( 1 , 0 ) ;
	L.preProcess () ;
	while ( m -- ) {
		scanf ( "%d%d" , &u , &v ) ;
		printf ( "%d\n" , L.dist[u] + L.dist[v] - 2 * L.dist[L.query ( u , v )] ) ;
	}
}

抱歉!评论已关闭.