LCA问题

一.概述:

  在图论与计算科学中,两个节点 与 有向无环图directed acyclic graph , DAG )或中的最近公共祖先(Lowest common anccestor , LCA ) 是这两个节点 v的深度最深的祖先。我们定义,该深度最深的节点为 v 与 w 的最近公共最先,即LCA 。

例如,在下图中

 浅谈 LCA

LCA ( A , B ) = F , LCA ( A , G ) = C , LCA ( B , D ) = C , LCA ( C , G ) = C ;

[ATTENTION]:两个节点的LCA在两点间的路径上。

二.求解方法

  方法一:

    将LCA问题转化为RMQ问题(区间最值问题)。

    1).从任意一个节点开始,对图进行深度优先遍历,记录每个节点的欧拉序,深度,第一次被遍历fst[]时间等信息。

    2).将每个节点的深度按照欧拉序列加入一个数组中,即每次经过一个节点时,都将其加入数组。

    3).如果fst[ v ] < fst[ u ] ,则LCA( v , u ) = RMQ ( fst[ v ] , fst[ u ] ) 

       如果fst[ v ] > fst[ u ] ,则LCA( v , u ) = RMQ ( fst[ u ] , fst[ v ] ) 

    以上就是主要思路,下面举个栗子:

    浅谈 LCA

    浅谈 LCA

 浅谈 LCA

     当查询A 与 D 的 LCA 时 , LCA ( A , D ) = RMQ ( fst[ D ] , fst[ A ] ) 

     即,区间 [ fst[ D ] , fst[ A ] ] 的深度最小值 , 这段区间[ 2 , 7 ] ,(2 ,1 ,2 ,1 ,2 ,3)中最小值是1,1对C应的节点是C,则A,D的LCA是C 。

     [ATTENTION]:区间最值的关键字是深度.

     那么现在的问题就是解决RMQ问题,通常使用ST算法(RMQ问题之ST算法)或线段树解决,本文使用线段树解决这个问题。

     核心代码&注释:

 

 1 inline void Add_Edge ( int x , int y , int _val ){//邻接表建图 
 2          e[ ++cnt ].to = y ;
 3          e[ cnt ].val = _val ;
 4          e[ cnt ].next = head[ x ] ;
 5          head[ x ] = cnt ;
 6 }
 7 
 8 void Build_Tree ( int x , int y , int i ) {//线段树建树 
 9          tr[ i ].l = x ; tr[ i ].r = y ;
10          if ( x==y ) tr[ i ].mintr = dep[ x ] , tr[ i ].pos = x ;//按照深度建树 
11          else {
12                   QAQ mid = ( tr[ i ].l + tr[ i ].r ) >>1 ;
13                  Build_Tree ( x , mid , i<<1);
14                  Build_Tree ( mid+1 , y , i<<1|1);
15                  if (tr[i<<1].mintr > tr[i<<1|1].mintr )//tr[].mintr表示这个区间最小值 ,tr[].pos表示最小值所在位置 
16                           tr[ i ].pos = tr[i<<1|1].pos,tr[ i ].mintr = tr[i<<1|1].mintr;
17                  else 
18                          tr[ i ].pos = tr[ i<<1 ].pos,tr[ i ].mintr = tr[ i<<1 ].mintr;
19          }
20          
21 }
22 
23 void DFS ( int x , int depth ) {
24          vis[ x ] = true ; 
25          ver[ ++dfs_num ] = x ; //欧拉序 
26          fst[ x ] = dfs_num ; //第一次出现位置 
27          dep[ dfs_num ] = depth ;//该节点深度 
28          for ( int i=head[ x ] ; i ; i=e[i].next ) {
29                   int temp = e[ i ].to ;
30                   if ( !vis[ temp ] ){
31                            DFS ( temp , depth + 1 ) ;
32                            ver[ ++dfs_num ] = x ; 
33                            dep[ dfs_num ] = depth ;
34                  }
35          }
36 }
37 
38 void Query_Min ( int q , int w , int i ) {
39          if(q <= tr[i].l && w >= tr[i].r ){
40                   if (tr[ i ].mintr < min_val ){
41                            min_val = tr[i].mintr ;// 记录最小值 
42                            min_pos = tr[i].pos ;// 记录最小值所在位置 
43                   }
44          }
45          else {
46                   QAQ mid = (tr[i].l + tr[i].r ) >> 1;
47                   if(q > mid) {
48                           Query_Min ( q , w , i << 1 | 1);
49                   }
50                   else if(w <= mid) {
51                           Query_Min ( q , w , i << 1);
52                   }
53                   else {
54                           Query_Min ( q , w , i << 1) ;
55                           Query_Min ( q , w , i << 1 | 1);
56                   }
57          }
58 }
59 
60 int LCA ( int x , int y ) {
61          int px = fst[ x ] , py = fst[ y ] , tmp ;
62          min_val = INF ;//初始化 
63          if ( py < px ) swap ( px , py ) ;
64          Query_Min ( px , py , 1 ) ;
65          return ver[ min_pos ] ;//最小值在欧拉序中对应节点即为LCA 
66 }
67 int main ( ) {
68          int N ,M ;
69          scanf ("%d",&N);
70          for ( int i=1 ; i<=N-1 ; ++i ) {
71                   int _x , _y , __ ;
72                   scanf("%d %d %d" , &_x , &_y ,&__ ) ;
73                   Add_Edge ( _x , _y , __ ) ;
74                   Add_Edge ( _y , _x , __ ) ;
75          }
76          DFS ( 1 , 1 ) ;
77          Build_Tree ( 1 , dfs_num , 1 ) ;
78          DEBUG_( dfs_num ) ;
79          scanf ("%d",&M);
80          for ( int i=1 ; i<=M ; ++i ) {
81                   int u , v ;
82                   scanf ( "%d%d" , &u , &v ) ;
83                   printf ("%d",LCA ( u , v ) ) ;
84                   putchar('\n');
85          }
86          return 0 ;
87 }
LCA->RMQ

相关文章:

  • 2021-11-27
  • 2022-12-23
  • 2021-11-19
  • 2021-11-29
  • 2022-01-07
  • 2021-06-26
  • 2021-12-28
  • 2021-05-06
猜你喜欢
  • 2022-12-23
  • 2021-09-10
  • 2021-09-11
  • 2022-12-23
  • 2021-08-29
  • 2021-09-26
  • 2021-08-13
相关资源
相似解决方案