【ZOJ】3820 Building Fire Stations 树的直径

来源:互联网 发布:知恩中学美术班好吗 编辑:程序博客网 时间:2024/06/06 05:41

传送门:【ZOJ】3820  Building Fire Stations


题目分析:求三次树的直径即可得到解。。。

先求第一次得到整棵树的直径,找到直径中点,将树“掰”成两棵,分别求这两棵树的直径,最大距离即两棵子树直径除以2中的最大值,选择的结点即两棵子树各自的直径中点。

首先,我们要明确选择的两个点一定在直径上,如果有一个不在直径上,则我们总可以通过将其移到直径上而使结果不会变劣(可能变优),这个可以自己画图YY一下。

我们这样考虑,如果求完第一次直径以后,以直径的其中一个端点为根遍历得到整棵树,对于树上某一点,所有不是他的子树内的点到他的距离均不超过该点到根的距离(否则直径的端点就不是当前的根了)。而在他子树内的点,随着选择的点沿着直径不断下降,到这个点的距离不断减少,而该点到根的距离不断增加(该点到根上的其他点连接出去的子树均可以忽略,因为他们到该点的距离不会超过根到该点的距离),我们就是要找到一个平衡点使得无论该点下降还是上升,其最大距离都会变大。

由于我们可以设两个平衡点,所以我们可以将整棵树沿着直径的中点分成两棵,分别添加一个平衡点,这样结果一定不会变劣。易知,我们要找的平衡点就是子树的中点!所以我们只要对子树再分别求得其直径,以及中点即可!


代码如下:


#include <cstdio>#include <cstring>#include <algorithm>using namespace std ;#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 )typedef long long LL ;const int MAXN = 200005 ;const int MAXE = 1000005 ;struct Edge {int v ;Edge* next ;} E[MAXE] , *H[MAXN] , *edge ;int dep[MAXN] ;int pre[MAXN] ;int vis[MAXN] ;int Q[MAXN] , head , tail ;int n ;int max_dis , idx ;void clear () {edge = E ;clr ( H , 0 ) ;clr ( vis , 0 ) ;}void addedge ( int u , int v ) {edge->v = v ;edge->next = H[u] ;H[u] = edge ++ ;}void get_dis ( int s ) {dep[s] = 1 ;head = tail = 0 ;Q[tail ++] = s ;max_dis = 1 ;idx = s ;pre[s] = 0 ;while ( head != tail ) {int u = Q[head ++] ;travel ( e , H , u ) {int v = e->v ;if ( vis[v] ) continue ;if ( v == pre[u] ) continue ;pre[v] = u ;dep[v] = dep[u] + 1 ;Q[tail ++] = v ;if ( dep[v] > max_dis ) {max_dis = dep[v] ;idx = v ;}}}}void find ( int u ) {get_dis ( u ) ;get_dis ( idx ) ;}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 u , v ;clear () ;scanf ( "%d" , &n ) ;rep ( i , 1 , n ) {scanf ( u ) , scanf ( v ) ;addedge ( u , v ) ;addedge ( v , u ) ;}//tot treefind ( 1 ) ;int len = max_dis ;int ans = 0 ;int x = idx ;while ( 1 ) {if ( dep[x] == len / 2 + 1 ) break ;else x = pre[x] ;}//first treevis[x] = 1 ;find ( pre[x] ) ;int len1 = max_dis , node1 = idx ;ans = max ( ans , len1 / 2 ) ;while ( 1 ) {if ( dep[node1] == len1 / 2 + 1 ) break ;else node1 = pre[node1] ;}//second treevis[x] = 0 ;vis[pre[x]] = 1 ;find ( x ) ;int len2 = max_dis , node2 = idx ;ans = max ( ans , len2 / 2 ) ;while ( 1 ) {if ( dep[node2] == len2 / 2 + 1 ) break ;else node2 = pre[node2] ;}printf ( "%d %d %d\n" , ans , node1 , node2 ) ;}int main () {int T ;scanf ( "%d" , &T ) ;while ( T -- ) solve () ;return 0 ;}


0 0