最近公共祖先LCA Tarjan算法

最近公共祖先LCA Tarjan算法_第1张图片

在求解最近公共祖先为问题上,用到的是Tarjan的思想,从根结点开始形成一棵深树,非常好的处理技巧就是在回溯到结点u的时候,u的子树已经遍历,这时候才把u结点放入合并集合中,这样u结点和所有u的子树中的结点的最近公共祖先就是u了,u和还未遍历的所有u的兄弟结点及子树中的最近公共祖先就是u的父亲结点。以此类推。。这样我们在对树深度遍历的时候就很自然的将树中的结点分成若干的集合,两个集合中的所属不同集合的任意一对顶点的公共祖先都是相同的,也就是说这两个集合的最近公共最先只有一个。对于每个集合而言可以用并查集来优化,时间复杂度就大大降低了,为O(n + q),n为总结点数,q为询问结点对数。

http://acm.hdu.edu.cn/showproblem.php?pid=2586


const int Max_N = 40008 ;
const int Max_M = 208  ;

struct  Edge{
        int  v ;
        int  w ;
        int  next ;
}edge[Max_N*2];

int  id  , List[Max_N] ;

void  add_edge(int u , int v  , int w){
      edge[id].v = v ;
      edge[id].w = w ;
      edge[id].next = List[u] ;
      List[u] = id++ ;
}

struct  Q{
        int v  ;
        int id ;
        Q(){}
        Q(int _v , int _id):v(_v),id(_id){}
};

vector<Q> Query[Max_N] ;
bool visited[Max_N] ;
int  dist[Max_N] ;
int  father[Max_N] ;
int  lca[Max_M] ;
int  q[Max_M][2] ;

int  find_father(int x){
     if(x == father[x]) return x ;
     return father[x] = find_father(father[x]) ;
}

void  tarjan(int u){
      int e , v , w , i  , qv , qid ;
      visited[u] = 1 ;
      for(e = List[u] ; e != -1 ; e = edge[e].next){
            v = edge[e].v ;
            w = edge[e].w ;
            for(i = 0 ; i < Query[u].size() ; i++){
                 qv = Query[u][i].v ;
                 qid = Query[u][i].id ;
                 if(visited[qv])
                     lca[qid] = find_father(qv) ;
            }
            if(! visited[v]){
               dist[v] = dist[u] + w ;
               tarjan(v) ;
               father[v] = u ;
            }
      }
}

int  main(){
     int T , u , v , w  , i  , N , M ;
     cin>>T ;
     while(T--){
          cin>>N>>M ;
          id = 0 ;
          memset(List , -1 , (N+1)*sizeof(int)) ;
          for(i = 1 ; i <= N ; i++){
                Query[i].clear() ;
                father[i] = i ;
          }
          for(i = 1 ; i < N ; i++){
              scanf("%d%d%d" ,&u , &v , &w) ;
              add_edge(u , v , w)  ;
              add_edge(v , u , w)  ;
          }
          for(i = 1 ; i <= M ; i++){
                scanf("%d%d" ,&u ,&v)  ;
                Query[u].push_back(Q(v , i)) ;
                Query[v].push_back(Q(u , i)) ;
                q[i][0] = u ;
                q[i][1] = v ;
          }
          memset(visited , 0 , (N+1)*sizeof(bool)) ;
          dist[1] = 0 ;
          tarjan(1) ;
          for(i = 1 ; i <= M ; i++){
              u = q[i][0] ;
              v = q[i][1] ;
              printf("%d\n" , dist[u] + dist[v] - 2*dist[lca[i]]) ;
          }
     }
     return 0 ;
}






你可能感兴趣的:(最近公共祖先LCA Tarjan算法)