給出你一棵樹,最後一行詢問頂點a和頂點b的最近公用祖先
Tarjan離線尋找最近公用祖先:
搜到新的頂點,此頂點的臨時祖先就是上一層的頂點
直到搜到葉子就開始回溯,回溯的時候
從這點出發搜過的頂點的臨時祖先合并為這個頂點的上一層頂點
//Final LCA離線演算法求最近公用祖先#include <stdio.h>#include <string.h>#include <stdlib.h>#include <algorithm>#include <vector>#define MAX 11000using namespace std;vector<int>Hash[MAX],Qes[MAX];int n,m,visit[MAX],ansetor[MAX],parent[MAX],fathernum[MAX];void Init(int n) //並查集初始化{ for(int i=1;i<=n;i++) parent[i]=i;}int Find(int x) //並查集尋找和壓縮路徑{ int s,j; s=x; while(x!=parent[x]) x=parent[x]; while(s!=x) { j=parent[s]; parent[s]=x; s=j; } return x;}void Union(int r1,int r2) //並查集合并{ int R1,R2; R1=Find(r1); R2=Find(r2); if(R1!=R2) parent[R1]=R2;}void LCA(int u) //LCA{ int i,size; visit[u]=1; ansetor[u]=u; size=Hash[u].size(); for(i=0;i<size;i++) //size()從0開始計算 {if(!visit[Hash[u][i]]){ LCA(Hash[u][i]); Union(u,Hash[u][i]); ansetor[Find(Hash[u][i])]=u; } //***可以是Find(u)或者Find(Hash[u][i]),因為已經合并了 } size=Qes[u].size(); for(i=0;i<size;i++) //size()從0開始計算 { if(visit[Qes[u][i]]) //如果需要尋找的兩個點其中一個點之前被訪問過, //那麼此時它的祖先就是它們的最近公用祖先 { m=ansetor[Find(Qes[u][i])]; //***只能是Find(Qes[u][i]),因為此時u和Qes[u][i]並未合并 return ; } }}int main(){ int a,b,i,t; scanf("%d",&t); while(t--) { scanf("%d",&n); Init(n); memset(visit,0,sizeof(visit)); memset(fathernum,0,sizeof(fathernum)); for(i=1;i<=n;i++) { Qes[i].clear(); Hash[i].clear(); } for(i=1;i<=n-1;i++) { scanf("%d%d",&a,&b); Hash[a].push_back(b); //表示a是b的父親 fathernum[b]++; //記錄每個頂點父親的個數 } scanf("%d%d",&a,&b); Qes[a].push_back(b); //需要尋找的兩點 Qes[b].push_back(a); //需要尋找的兩點 for(i=1;i<=n;i++) { if(!fathernum[i]) //沒有父親結點的點既是整棵樹的根節點 { LCA(i);printf("%d\n",m); break; } } } return 0;}