zoukankan      html  css  js  c++  java
  • 牡丹江.2014B(图论,树的直径)

    B - Building Fire Stations
    Time Limit:5000MS     Memory Limit:131072KB     64bit IO Format:%lld & %llu
    Appoint description: 

    Description

    Marjar University is a beautiful and peaceful place. There are N buildings and N - 1 bidirectional roads in the campus. These buildings are connected by roads in such a way that there is exactly one path between any two buildings. By coincidence, the length of each road is 1 unit.

    To ensure the campus security, Edward, the headmaster of Marjar University, plans to setup two fire stations in two different buildings so that firefighters are able to arrive at the scene of the fire as soon as possible whenever fires occur. That means the longest distance between a building and its nearest fire station should be as short as possible.

    As a clever and diligent student in Marjar University, you are asked to write a program to complete the plan. Please find out two proper buildings to setup the fire stations.

    Input

    There are multiple test cases. The first line of input contains an integer T indicating the number of test cases. For each test case:

    The first line contains an integer N (2 <= N <= 200000).

    For the next N - 1 lines, each line contains two integers Xi and Yi. That means there is a road connecting building Xi and building Yi(indexes are 1-based).

    Output

    For each test case, output three integers. The first one is the minimal longest distance between a building and its nearest fire station. The next two integers are the indexes of the two buildings selected to build the fire stations.

    If there are multiple solutions, any one will be acceptable.

    Sample Input

    2
    4
    1 2
    1 3
    1 4
    5
    1 2
    2 3
    3 4
    4 5
    

    Sample Output

    1 1 2
    1 2 4
    
     
     1 //#pragma comment(linker, "/STACK:1024000000,1024000000") 
     2 using namespace std ;
     3 const int M = 2e5 + 10 , inf = 111111111 ;
     4 struct node {
     5         int L , R ;
     6         int dl , dr ;
     7         node () {}
     8         node (int L , int R , int dl , int dr) :
     9                 L(L) , R(R) , dl(dl) , dr(dr) {}
    10 }a[M] ;
    11 int id , maxn ;
    12 int n ;
    13 vector<int> g[M] ;
    14 bool vis[M] ;
    15 vector<int> path ;
    16 int N ;
    17 int dfs (int o , int u) {
    18         a[u] = node (u , u , 1 , 1) ;
    19         for (vector<int>::iterator v = g[u].begin () ; v != g[u].end () ; v ++) {
    20                 if (*v == o) continue ;
    21                 int tmp = 1 + dfs (u , *v) ;
    22                 if (tmp > a[u].dl) a[u] = node (a[*v].L , a[u].L , tmp , a[u].dl) ;
    23                 else if (tmp > a[u].dr) a[u].R = a[*v].L , a[u].dr = tmp ;
    24         }
    25         if (a[u].dl + a[u].dr > maxn) maxn = a[u].dl + a[u].dr , id = u ;
    26         return a[u].dl ;
    27 }
    28 
    29 bool PATH (int o , int u) {
    30         for (vector<int>::iterator v = g[u].begin() ; v != g[u].end () ; v ++) {
    31                 if (*v == o) continue ;
    32                 if (PATH (u , *v)) {path.push_back (u) ; return true ;} 
    33         }
    34         if (u == a[id].R) {path.push_back (u) ; return true ;} 
    35         return false ;
    36 }
    37 
    38 void DFS (int o , int u , int dep , int deep) {
    39         if (dep > deep) return ;
    40         vis[u] = 1 ;
    41         for (vector<int>::iterator v = g[u].begin() ; v != g[u].end () ; v ++) {
    42                 if (*v == o) continue ;
    43                 DFS (u , *v , dep+1 , deep) ;
    44         }
    45 }
    46 
    47 bool judge (int x) {
    48         memset (vis , 0 , sizeof(vis)) ;
    49         DFS (-1 , path[x] , 0 ,  x ) ;
    50         DFS (-1 , N-x-1 == x ? path[x+1] : path[N-x-1] , 0 , x ) ;
    51         //printf ("x = %d
    " , x ) ;
    52         //if (x == 1) for (int i = 1 ; i <= n ; i ++) if (!vis[i]) printf ("geng shi %d
    " , i ) ;
    53         for (int i = 1 ; i <= n ; i ++) if (!vis[i]) return false ;
    54         return true ;
    55 }
    56 
    57 void solve () {
    58         N = path.size () ;
    59         int l = 0 , r = N / 2 ;
    60         int ret = r ;
    61         //printf ("l = %d , r = %d
    " , l , r) ;
    62         while (l <= r) {
    63                 int mid = l + r >> 1 ;
    64                 if (judge (mid)) {
    65                         ret = mid ;
    66                         r = mid - 1 ;
    67                         //printf ("ret = %d
    " , ret ) ;
    68                 }
    69                 else l = mid + 1 ;
    70         }
    71         printf ("%d %d %d
    " , ret , path[ret] , ret == N-ret-1 ? path[ret+1] : path[N-ret-1] ) ;  
    72 }
    73 
    74 int main () {
    75         int T ;
    76         scanf ("%d" , &T ) ;
    77         while (T --) {
    78                 scanf ("%d" , &n) ;
    79                 path.clear () ;
    80                 for (int i = 0 ; i <= n ; i ++) g[i].clear () ;
    81                 for (int i = 0 ; i < n - 1 ; i ++) {
    82                         int u , v ;
    83                         scanf ("%d%d" , &u , &v) ;
    84                         g[u].push_back (v) ;
    85                         g[v].push_back (u) ;
    86                 }
    87 
    88                 maxn = -1 ;
    89                 dfs (-1 , 1) ;
    90                 //printf ("Dia = %d , Lnode = %d , Rnode = %d
    " , maxn-1 , a[id].L , a[id].R) ;
    91                 PATH (-1 , a[id].L) ;
    92                 //for (vector<int>::iterator it = path.begin() ; it != path.end() ; it ++) printf ("%d " , *it) ; puts ("") ;
    93                 solve () ;
    94         }
    95         return 0 ;
    96 }
    View Code

    因为可行的两个点肯定是在树的直径上(对称的两点),所以我们可以二分答案,最优解肯定在0~dia/2 内。

    另外找直径的方法可以换一下,可以从任意一点搜到任意一个深度最大的点v,在从v开始搜搜到此时深度最大的点u,则u,v就是树直径的两个端点。

    我的方法是:

    对于任何一个节点,他都有一个最深,和次深子节点L,R。所以从任意一个点u出发,用u的两个最深的子节点来更新,u.dl= v1.dl+1 , u.dr = v2.dl + 1 ;

    u.L = v1 , u.R = v2 (深度(V1) >= 深度 (V2) ) ;

    这样找一遍dfs就可以知道Dia是多少,并且知到该直径的两个端点分别是谁。

    缺点是打印Dia路径时,需要再写一个dfs,hhhhh

    还要注意,zoj坑爹的栈深度很低,20w就爆了,所以想要dfs过的话,就去cf上交吧:http://codeforces.com/gym/100554/problem/B 

  • 相关阅读:
    装饰者模式(包装模式)
    内网穿透
    SpringMVC框架工作流程图及工作原理
    Java 使用 Socket 判断某服务能否连通
    最简单的一个socket客户端,保持连接服务端
    java读取txt文件
    SQL汇总
    最简单的一个socket服务端
    初级软件工程师怎么走向BATJ?——献给迷茫中的测试人
    软件测试工程师——100问面试题,你敢来接招吗?
  • 原文地址:https://www.cnblogs.com/get-an-AC-everyday/p/4737863.html
Copyright © 2011-2022 走看看